mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into ys_extruders_color
This commit is contained in:
		
						commit
						a0b46a4019
					
				
					 50 changed files with 538 additions and 637 deletions
				
			
		|  | @ -90,7 +90,7 @@ struct stl_neighbors { | |||
| 
 | ||||
| struct stl_stats { | ||||
|     stl_stats() { memset(&header, 0, 81); } | ||||
|     char          header[81]                = ""; | ||||
|     char          header[81]; | ||||
|     stl_type      type                      = (stl_type)0; | ||||
|     uint32_t      number_of_facets          = 0; | ||||
|     stl_vertex    max                       = stl_vertex::Zero(); | ||||
|  |  | |||
|  | @ -100,7 +100,7 @@ add_library(libslic3r STATIC | |||
|     Geometry.cpp | ||||
|     Geometry.hpp | ||||
|     Int128.hpp | ||||
|     KdTreeIndirect.hpp | ||||
|     KDTreeIndirect.hpp | ||||
|     Layer.cpp | ||||
|     Layer.hpp | ||||
|     LayerRegion.cpp | ||||
|  | @ -131,8 +131,6 @@ add_library(libslic3r STATIC | |||
|     PolygonTrimmer.hpp | ||||
|     Polyline.cpp | ||||
|     Polyline.hpp | ||||
|     PolylineCollection.cpp | ||||
|     PolylineCollection.hpp | ||||
|     Print.cpp | ||||
|     Print.hpp | ||||
|     PrintBase.cpp | ||||
|  |  | |||
|  | @ -13,12 +13,12 @@ typedef std::vector<ExPolygonCollection> ExPolygonCollections; | |||
| 
 | ||||
| class ExPolygonCollection | ||||
| { | ||||
|     public: | ||||
| public: | ||||
|     ExPolygons expolygons; | ||||
|      | ||||
|     ExPolygonCollection() {}; | ||||
|     ExPolygonCollection() {} | ||||
|     ExPolygonCollection(const ExPolygon &expolygon); | ||||
|     ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {}; | ||||
|     ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {} | ||||
|     operator Points() const; | ||||
|     operator Polygons() const; | ||||
|     operator ExPolygons&(); | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ class ExtrusionEntityCollection; | |||
| class Extruder; | ||||
| 
 | ||||
| // Each ExtrusionRole value identifies a distinct set of { extruder, speed }
 | ||||
| enum ExtrusionRole { | ||||
| enum ExtrusionRole : uint8_t { | ||||
|     erNone, | ||||
|     erPerimeter, | ||||
|     erExternalPerimeter, | ||||
|  | @ -81,8 +81,8 @@ public: | |||
|     virtual ExtrusionEntity* clone_move() = 0; | ||||
|     virtual ~ExtrusionEntity() {} | ||||
|     virtual void reverse() = 0; | ||||
|     virtual Point first_point() const = 0; | ||||
|     virtual Point last_point() const = 0; | ||||
|     virtual const Point& first_point() const = 0; | ||||
|     virtual const Point& last_point() const = 0; | ||||
|     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
 | ||||
|     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
 | ||||
|     virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0; | ||||
|  | @ -117,30 +117,23 @@ public: | |||
|     float width; | ||||
|     // Height of the extrusion, used for visualization purposes.
 | ||||
|     float height; | ||||
|     // Feedrate of the extrusion, used for visualization purposes.
 | ||||
|     float feedrate; | ||||
|     // Id of the extruder, used for visualization purposes.
 | ||||
|     unsigned int extruder_id; | ||||
|     // Id of the color, used for visualization purposes in the color printing case.
 | ||||
|     unsigned int cp_color_id; | ||||
| 
 | ||||
|     ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} | ||||
|     ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} | ||||
|     ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} | ||||
| 	ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} | ||||
| 	ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} | ||||
| 	ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} | ||||
| //    ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {};
 | ||||
|     ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {}; | ||||
|     ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}; | ||||
|     ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} | ||||
|     ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} | ||||
|     ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} | ||||
|     ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} | ||||
| 
 | ||||
|     ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = rhs.polyline; return *this; } | ||||
|     ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = std::move(rhs.polyline); return *this; } | ||||
|     ExtrusionPath& operator=(const ExtrusionPath& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = rhs.polyline; return *this; } | ||||
|     ExtrusionPath& operator=(ExtrusionPath&& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = std::move(rhs.polyline); return *this; } | ||||
| 
 | ||||
| 	ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); } | ||||
|     // Create a new object, initialize it with this object using the move semantics.
 | ||||
| 	ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); } | ||||
|     void reverse() override { this->polyline.reverse(); } | ||||
|     Point first_point() const override { return this->polyline.points.front(); } | ||||
|     Point last_point() const override { return this->polyline.points.back(); } | ||||
|     const Point& first_point() const override { return this->polyline.points.front(); } | ||||
|     const Point& last_point() const override { return this->polyline.points.back(); } | ||||
|     size_t size() const { return this->polyline.size(); } | ||||
|     bool empty() const { return this->polyline.empty(); } | ||||
|     bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); } | ||||
|  | @ -200,8 +193,8 @@ public: | |||
|     // Create a new object, initialize it with this object using the move semantics.
 | ||||
| 	ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); } | ||||
|     void reverse() override; | ||||
|     Point first_point() const override { return this->paths.front().polyline.points.front(); } | ||||
|     Point last_point() const override { return this->paths.back().polyline.points.back(); } | ||||
|     const Point& first_point() const override { return this->paths.front().polyline.points.front(); } | ||||
|     const Point& last_point() const override { return this->paths.back().polyline.points.back(); } | ||||
|     double length() const override; | ||||
|     ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } | ||||
|     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
 | ||||
|  | @ -243,8 +236,8 @@ public: | |||
|     bool make_clockwise(); | ||||
|     bool make_counter_clockwise(); | ||||
|     void reverse() override; | ||||
|     Point first_point() const override { return this->paths.front().polyline.points.front(); } | ||||
|     Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); } | ||||
|     const Point& first_point() const override { return this->paths.front().polyline.points.front(); } | ||||
|     const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } | ||||
|     Polygon polygon() const; | ||||
|     double length() const override; | ||||
|     bool split_at_vertex(const Point &point); | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #include "ExtrusionEntityCollection.hpp" | ||||
| #include "ShortestPath.hpp" | ||||
| #include <algorithm> | ||||
| #include <cmath> | ||||
| #include <map> | ||||
|  | @ -73,78 +74,31 @@ void ExtrusionEntityCollection::remove(size_t i) | |||
|     this->entities.erase(this->entities.begin() + i); | ||||
| } | ||||
| 
 | ||||
| ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const | ||||
| ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const | ||||
| { | ||||
|     ExtrusionEntityCollection coll; | ||||
|     this->chained_path(&coll, no_reverse, role); | ||||
|     return coll; | ||||
| } | ||||
| 
 | ||||
| void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const | ||||
| { | ||||
|     if (this->entities.empty()) return; | ||||
|     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 coll; | ||||
|     this->chained_path_from(start_near, &coll, no_reverse, role); | ||||
|     return coll; | ||||
| } | ||||
| 
 | ||||
| void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const | ||||
| { | ||||
|     if (this->no_sort) { | ||||
|         *retval = *this; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     retval->entities.reserve(this->entities.size()); | ||||
|      | ||||
|     // if we're asked to return the original indices, build a map
 | ||||
|     std::map<ExtrusionEntity*,size_t> indices_map; | ||||
|      | ||||
|     ExtrusionEntitiesPtr my_paths; | ||||
|     for (ExtrusionEntity * const &entity_src : this->entities) { | ||||
|         if (role != erMixed) { | ||||
|             // The caller wants only paths with a specific extrusion role.
 | ||||
|             auto role2 = entity_src->role(); | ||||
|             if (role != role2) { | ||||
|                 // This extrusion entity does not match the role asked.
 | ||||
|                 assert(role2 != erMixed); | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         ExtrusionEntity *entity = entity_src->clone(); | ||||
|         my_paths.push_back(entity); | ||||
| //        if (orig_indices != nullptr)
 | ||||
| //        	indices_map[entity] = &entity_src - &this->entities.front();
 | ||||
|     } | ||||
|      | ||||
|     Points endpoints; | ||||
|     for (const ExtrusionEntity *entity : my_paths) { | ||||
|         endpoints.push_back(entity->first_point()); | ||||
|         endpoints.push_back((no_reverse || ! entity->can_reverse()) ? | ||||
|         	entity->first_point() : entity->last_point()); | ||||
|     } | ||||
|      | ||||
|     while (! my_paths.empty()) { | ||||
|         // find nearest point
 | ||||
|         int start_index = start_near.nearest_point_index(endpoints); | ||||
|         int path_index = start_index/2; | ||||
|         ExtrusionEntity* entity = my_paths.at(path_index); | ||||
|         // never reverse loops, since it's pointless for chained path and callers might depend on orientation
 | ||||
|         if (start_index % 2 && !no_reverse && entity->can_reverse()) | ||||
|             entity->reverse(); | ||||
|         retval->entities.push_back(my_paths.at(path_index)); | ||||
| //        if (orig_indices != nullptr)
 | ||||
| //        	orig_indices->push_back(indices_map[entity]);
 | ||||
|         my_paths.erase(my_paths.begin() + path_index); | ||||
|         endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); | ||||
|         start_near = retval->entities.back()->last_point(); | ||||
|     } | ||||
| 	ExtrusionEntityCollection out; | ||||
| 	if (this->no_sort) { | ||||
| 		out = *this; | ||||
| 	} else { | ||||
| 		if (role == erMixed) | ||||
| 			out = *this; | ||||
| 		else { | ||||
| 		    for (const ExtrusionEntity *ee : this->entities) { | ||||
| 		        if (role != erMixed) { | ||||
| 		            // The caller wants only paths with a specific extrusion role.
 | ||||
| 		            auto role2 = ee->role(); | ||||
| 		            if (role != role2) { | ||||
| 		                // This extrusion entity does not match the role asked.
 | ||||
| 		                assert(role2 != erMixed); | ||||
| 		                continue; | ||||
| 		            } | ||||
| 		        } | ||||
| 		        out.entities.emplace_back(ee->clone()); | ||||
| 		    } | ||||
| 		} | ||||
| 		chain_and_reorder_extrusion_entities(out.entities, &start_near); | ||||
| 	} | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const | ||||
|  |  | |||
|  | @ -65,13 +65,10 @@ public: | |||
|     } | ||||
|     void replace(size_t i, const ExtrusionEntity &entity); | ||||
|     void remove(size_t i); | ||||
|     ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) 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; | ||||
|     void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const; | ||||
|     ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const; | ||||
|     void reverse(); | ||||
|     Point first_point() const { return this->entities.front()->first_point(); } | ||||
|     Point last_point() const { return this->entities.back()->last_point(); } | ||||
|     const Point& first_point() const { return this->entities.front()->first_point(); } | ||||
|     const Point& last_point() const { return this->entities.back()->last_point(); } | ||||
|     // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
 | ||||
|     // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
 | ||||
|     void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; | ||||
|  |  | |||
|  | @ -176,7 +176,7 @@ void Fill3DHoneycomb::_fill_surface_single( | |||
|             } | ||||
|         } | ||||
|         bool first = true; | ||||
|         for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { | ||||
|         for (Polyline &polyline : chain_polylines(std::move(polylines))) { | ||||
|             if (! first) { | ||||
|                 // Try to connect the lines.
 | ||||
|                 Points &pts_end = polylines_out.back().points; | ||||
|  |  | |||
|  | @ -167,7 +167,7 @@ void FillGyroid::_fill_surface_single( | |||
|             } | ||||
|         } | ||||
|         bool first = true; | ||||
|         for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { | ||||
|         for (Polyline &polyline : chain_polylines(std::move(polylines))) { | ||||
|             if (! first) { | ||||
|                 // Try to connect the lines.
 | ||||
|                 Points &pts_end = polylines_out.back().points; | ||||
|  |  | |||
|  | @ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single( | |||
| 
 | ||||
|         // connect paths
 | ||||
|         if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
 | ||||
|             Polylines chained = chain_infill_polylines(std::move(paths)); | ||||
|             Polylines chained = chain_polylines(std::move(paths)); | ||||
|             assert(paths.empty()); | ||||
|             paths.clear(); | ||||
|             for (Polyline &path : chained) { | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| #include "../ClipperUtils.hpp" | ||||
| #include "../PolylineCollection.hpp" | ||||
| #include "../Surface.hpp" | ||||
| 
 | ||||
| #include "FillPlanePath.hpp" | ||||
|  |  | |||
|  | @ -93,7 +93,7 @@ void FillRectilinear::_fill_surface_single( | |||
|             } | ||||
|         } | ||||
|         bool first = true; | ||||
|         for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) { | ||||
|         for (Polyline &polyline : chain_polylines(std::move(polylines))) { | ||||
|             if (! first) { | ||||
|                 // Try to connect the lines.
 | ||||
|                 Points &pts_end = polylines_out.back().points; | ||||
|  |  | |||
|  | @ -117,11 +117,11 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP | |||
|                      const Layer* layer1 = object->layers()[i * 2]; | ||||
|                      const Layer* layer2 = object->layers()[i * 2 + 1]; | ||||
|                      Polygons polys; | ||||
|                      polys.reserve(layer1->slices.expolygons.size() + layer2->slices.expolygons.size()); | ||||
|                     for (const ExPolygon &expoly : layer1->slices.expolygons) | ||||
|                      polys.reserve(layer1->slices.size() + layer2->slices.size()); | ||||
|                     for (const ExPolygon &expoly : layer1->slices) | ||||
|                         //FIXME no holes?
 | ||||
|                         polys.emplace_back(expoly.contour); | ||||
|                     for (const ExPolygon &expoly : layer2->slices.expolygons) | ||||
|                     for (const ExPolygon &expoly : layer2->slices) | ||||
|                         //FIXME no holes?
 | ||||
|                         polys.emplace_back(expoly.contour); | ||||
|                      polygons_per_layer[i] = union_(polys); | ||||
|  | @ -130,8 +130,8 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP | |||
|          if (object->layers().size() & 1) { | ||||
|             const Layer *layer = object->layers().back(); | ||||
|             Polygons polys; | ||||
|             polys.reserve(layer->slices.expolygons.size()); | ||||
|             for (const ExPolygon &expoly : layer->slices.expolygons) | ||||
|             polys.reserve(layer->slices.size()); | ||||
|             for (const ExPolygon &expoly : layer->slices) | ||||
|                 //FIXME no holes?
 | ||||
|                 polys.emplace_back(expoly.contour); | ||||
|              polygons_per_layer.back() = union_(polys); | ||||
|  | @ -1802,11 +1802,8 @@ void GCode::process_layer( | |||
|             // - for each island, we extrude perimeters first, unless user set the infill_first
 | ||||
|             //   option
 | ||||
|             // (Still, we have to keep track of regions because we need to apply their config)
 | ||||
|             size_t n_slices = layer.slices.expolygons.size(); | ||||
|             std::vector<BoundingBox> layer_surface_bboxes; | ||||
|             layer_surface_bboxes.reserve(n_slices); | ||||
|             for (const ExPolygon &expoly : layer.slices.expolygons) | ||||
|                 layer_surface_bboxes.push_back(get_extents(expoly.contour)); | ||||
|             size_t n_slices = layer.slices.size(); | ||||
|             const std::vector<BoundingBox> &layer_surface_bboxes = layer.slices_bboxes; | ||||
|             // 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; | ||||
|  | @ -1822,7 +1819,7 @@ void GCode::process_layer( | |||
|                 const BoundingBox &bbox = layer_surface_bboxes[i]; | ||||
|                 return point(0) >= bbox.min(0) && point(0) < bbox.max(0) && | ||||
|                        point(1) >= bbox.min(1) && point(1) < bbox.max(1) && | ||||
|                        layer.slices.expolygons[i].contour.contains(point); | ||||
|                        layer.slices[i].contour.contains(point); | ||||
|             }; | ||||
| 
 | ||||
|             for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { | ||||
|  | @ -1969,7 +1966,7 @@ void GCode::process_layer( | |||
|                     m_layer = layers[instance_to_print.layer_id].support_layer; | ||||
|                     gcode += this->extrude_support( | ||||
|                         // 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)); | ||||
|                         instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role)); | ||||
|                     m_layer = layers[instance_to_print.layer_id].layer(); | ||||
|                 } | ||||
|                 for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) { | ||||
|  | @ -2418,7 +2415,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou | |||
|             static int iRun = 0; | ||||
|             SVG svg(debug_out_path("GCode_extrude_loop-%d.svg", iRun ++)); | ||||
|             if (m_layer->lower_layer != NULL) | ||||
|                 svg.draw(m_layer->lower_layer->slices.expolygons); | ||||
|                 svg.draw(m_layer->lower_layer->slices); | ||||
|             for (size_t i = 0; i < loop.paths.size(); ++ i) | ||||
|                 svg.draw(loop.paths[i].as_polyline(), "red"); | ||||
|             Polylines polylines; | ||||
|  | @ -2588,10 +2585,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy | |||
|     std::string gcode; | ||||
|     for (const ObjectByExtruder::Island::Region ®ion : by_region) { | ||||
|         m_config.apply(print.regions()[®ion - &by_region.front()]->config()); | ||||
|         for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) { | ||||
|         for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos).entities) { | ||||
|             auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill); | ||||
|             if (eec) { | ||||
| 				for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities) | ||||
| 				for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) | ||||
|                     gcode += this->extrude_entity(*ee, "infill"); | ||||
|             } else | ||||
|                 gcode += this->extrude_entity(*fill, "infill"); | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ static const unsigned int DEFAULT_EXTRUDER_ID = 0; | |||
| static const unsigned int DEFAULT_COLOR_PRINT_ID = 0; | ||||
| static const Slic3r::Vec3d DEFAULT_START_POSITION = Slic3r::Vec3d(0.0f, 0.0f, 0.0f); | ||||
| static const float DEFAULT_START_EXTRUSION = 0.0f; | ||||
| static const float DEFAULT_FAN_SPEED = 0.0f; | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -36,21 +37,23 @@ const float GCodeAnalyzer::Default_Height = 0.0f; | |||
| GCodeAnalyzer::Metadata::Metadata() | ||||
|     : extrusion_role(erNone) | ||||
|     , extruder_id(DEFAULT_EXTRUDER_ID) | ||||
|     , cp_color_id(DEFAULT_COLOR_PRINT_ID) | ||||
|     , mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm) | ||||
|     , width(GCodeAnalyzer::Default_Width) | ||||
|     , height(GCodeAnalyzer::Default_Height) | ||||
|     , feedrate(DEFAULT_FEEDRATE) | ||||
|     , fan_speed(DEFAULT_FAN_SPEED) | ||||
|     , cp_color_id(DEFAULT_COLOR_PRINT_ID) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id/* = 0*/) | ||||
| GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id/* = 0*/) | ||||
|     : extrusion_role(extrusion_role) | ||||
|     , extruder_id(extruder_id) | ||||
|     , mm3_per_mm(mm3_per_mm) | ||||
|     , width(width) | ||||
|     , height(height) | ||||
|     , feedrate(feedrate) | ||||
|     , fan_speed(fan_speed) | ||||
|     , cp_color_id(cp_color_id) | ||||
| { | ||||
| } | ||||
|  | @ -75,15 +78,18 @@ bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other) | |||
|     if (feedrate != other.feedrate) | ||||
|         return true; | ||||
| 
 | ||||
|     if (fan_speed != other.fan_speed) | ||||
|         return true; | ||||
| 
 | ||||
|     if (cp_color_id != other.cp_color_id) | ||||
|         return true; | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id/* = 0*/) | ||||
| GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id/* = 0*/) | ||||
|     : type(type) | ||||
|     , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, cp_color_id) | ||||
|     , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, fan_speed, cp_color_id) | ||||
|     , start_position(start_position) | ||||
|     , end_position(end_position) | ||||
|     , delta_extruder(delta_extruder) | ||||
|  | @ -133,6 +139,7 @@ void GCodeAnalyzer::reset() | |||
|     _set_feedrate(DEFAULT_FEEDRATE); | ||||
|     _set_start_position(DEFAULT_START_POSITION); | ||||
|     _set_start_extrusion(DEFAULT_START_EXTRUSION); | ||||
|     _set_fan_speed(DEFAULT_FAN_SPEED); | ||||
|     _reset_axes_position(); | ||||
|     _reset_cached_position(); | ||||
| 
 | ||||
|  | @ -259,6 +266,16 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi | |||
|                         _processM83(line); | ||||
|                         break; | ||||
|                     } | ||||
|                 case 106: // Set fan speed
 | ||||
|                     { | ||||
|                         _processM106(line); | ||||
|                         break; | ||||
|                     } | ||||
|                 case 107: // Disable fan
 | ||||
|                     { | ||||
|                         _processM107(line); | ||||
|                         break; | ||||
|                     } | ||||
|                 case 108: | ||||
|                 case 135: | ||||
|                     { | ||||
|  | @ -448,6 +465,24 @@ void GCodeAnalyzer::_processM83(const GCodeReader::GCodeLine& line) | |||
|     _set_e_local_positioning_type(Relative); | ||||
| } | ||||
| 
 | ||||
| void GCodeAnalyzer::_processM106(const GCodeReader::GCodeLine& line) | ||||
| { | ||||
|     if (!line.has('P')) | ||||
|     { | ||||
|         // The absence of P means the print cooling fan, so ignore anything else.
 | ||||
|         float new_fan_speed; | ||||
|         if (line.has_value('S', new_fan_speed)) | ||||
|             _set_fan_speed((100.0f / 256.0f) * new_fan_speed); | ||||
|         else | ||||
|             _set_fan_speed(100.0f); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GCodeAnalyzer::_processM107(const GCodeReader::GCodeLine& line) | ||||
| { | ||||
|     _set_fan_speed(0.0f); | ||||
| } | ||||
| 
 | ||||
| void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line) | ||||
| { | ||||
|     // These M-codes are used by MakerWare and Sailfish to change active tool.
 | ||||
|  | @ -726,6 +761,16 @@ float GCodeAnalyzer::_get_feedrate() const | |||
|     return m_state.data.feedrate; | ||||
| } | ||||
| 
 | ||||
| void GCodeAnalyzer::_set_fan_speed(float fan_speed_percentage) | ||||
| { | ||||
|     m_state.data.fan_speed = fan_speed_percentage; | ||||
| } | ||||
| 
 | ||||
| float GCodeAnalyzer::_get_fan_speed() const | ||||
| { | ||||
|     return m_state.data.fan_speed; | ||||
| } | ||||
| 
 | ||||
| void GCodeAnalyzer::_set_axis_position(EAxis axis, float position) | ||||
| { | ||||
|     m_state.position[axis] = position; | ||||
|  | @ -798,7 +843,7 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) | |||
| 
 | ||||
|     Vec3d start_position = _get_start_position() + extruder_offset; | ||||
|     Vec3d end_position = _get_end_position() + extruder_offset; | ||||
|     it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_cp_color_id()); | ||||
|     it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id()); | ||||
| } | ||||
| 
 | ||||
| bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const | ||||
|  | @ -821,7 +866,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ | |||
|             } | ||||
| 
 | ||||
|             // if layer not found, create and return it
 | ||||
|             layers.emplace_back(z, ExtrusionPaths()); | ||||
|             layers.emplace_back(z, GCodePreviewData::Extrusion::Paths()); | ||||
|             return layers.back(); | ||||
|         } | ||||
| 
 | ||||
|  | @ -830,13 +875,18 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ | |||
|             // if the polyline is valid, create the extrusion path from it and store it
 | ||||
|             if (polyline.is_valid()) | ||||
|             { | ||||
|                 ExtrusionPath path(data.extrusion_role, data.mm3_per_mm, data.width, data.height); | ||||
| 				auto& paths = get_layer_at_z(preview_data.extrusion.layers, z).paths; | ||||
| 				paths.emplace_back(GCodePreviewData::Extrusion::Path()); | ||||
| 				GCodePreviewData::Extrusion::Path &path = paths.back(); | ||||
|                 path.polyline = polyline; | ||||
| 				path.extrusion_role = data.extrusion_role; | ||||
| 				path.mm3_per_mm = data.mm3_per_mm; | ||||
| 				path.width = data.width; | ||||
| 				path.height = data.height; | ||||
|                 path.feedrate = data.feedrate; | ||||
|                 path.extruder_id = data.extruder_id; | ||||
|                 path.cp_color_id = data.cp_color_id; | ||||
| 
 | ||||
|                 get_layer_at_z(preview_data.extrusion.layers, z).paths.push_back(path); | ||||
|                 path.fan_speed = data.fan_speed; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | @ -854,6 +904,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ | |||
|     GCodePreviewData::Range width_range; | ||||
|     GCodePreviewData::Range feedrate_range; | ||||
|     GCodePreviewData::Range volumetric_rate_range; | ||||
|     GCodePreviewData::Range fan_speed_range; | ||||
| 
 | ||||
|     // to avoid to call the callback too often
 | ||||
|     unsigned int cancel_callback_threshold = (unsigned int)std::max((int)extrude_moves->second.size() / 25, 1); | ||||
|  | @ -888,6 +939,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ | |||
|             width_range.update_from(move.data.width); | ||||
|             feedrate_range.update_from(move.data.feedrate); | ||||
|             volumetric_rate_range.update_from(volumetric_rate); | ||||
|             fan_speed_range.update_from(move.data.fan_speed); | ||||
|         } | ||||
|         else | ||||
|             // append end vertex of the move to current polyline
 | ||||
|  | @ -906,6 +958,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ | |||
|     preview_data.ranges.width.update_from(width_range); | ||||
|     preview_data.ranges.feedrate.update_from(feedrate_range); | ||||
|     preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range); | ||||
|     preview_data.ranges.fan_speed.update_from(fan_speed_range); | ||||
| 
 | ||||
|     // we need to sort the layers by their z as they can be shuffled in case of sequential prints
 | ||||
|     std::sort(preview_data.extrusion.layers.begin(), preview_data.extrusion.layers.end(), [](const GCodePreviewData::Extrusion::Layer& l1, const GCodePreviewData::Extrusion::Layer& l2)->bool { return l1.z < l2.z; }); | ||||
|  |  | |||
|  | @ -54,10 +54,11 @@ public: | |||
|         float width;     // mm
 | ||||
|         float height;    // mm
 | ||||
|         float feedrate;  // mm/s
 | ||||
|         float fan_speed; // percentage
 | ||||
|         unsigned int cp_color_id; | ||||
| 
 | ||||
|         Metadata(); | ||||
|         Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id = 0); | ||||
|         Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id = 0); | ||||
| 
 | ||||
|         bool operator != (const Metadata& other) const; | ||||
|     }; | ||||
|  | @ -81,7 +82,7 @@ public: | |||
|         Vec3d end_position; | ||||
|         float delta_extruder; | ||||
| 
 | ||||
|         GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id = 0); | ||||
|         GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id = 0); | ||||
|         GCodeMove(EType type, const Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder); | ||||
|     }; | ||||
| 
 | ||||
|  | @ -171,6 +172,12 @@ private: | |||
|     // Set extruder to relative mode
 | ||||
|     void _processM83(const GCodeReader::GCodeLine& line); | ||||
| 
 | ||||
|     // Set fan speed
 | ||||
|     void _processM106(const GCodeReader::GCodeLine& line); | ||||
| 
 | ||||
|     // Disable fan
 | ||||
|     void _processM107(const GCodeReader::GCodeLine& line); | ||||
| 
 | ||||
|     // Set tool (MakerWare and Sailfish flavor)
 | ||||
|     void _processM108orM135(const GCodeReader::GCodeLine& line); | ||||
| 
 | ||||
|  | @ -233,6 +240,9 @@ private: | |||
|     void _set_feedrate(float feedrate_mm_sec); | ||||
|     float _get_feedrate() const; | ||||
| 
 | ||||
|     void _set_fan_speed(float fan_speed_percentage); | ||||
|     float _get_fan_speed() const; | ||||
| 
 | ||||
|     void _set_axis_position(EAxis axis, float position); | ||||
|     float _get_axis_position(EAxis axis) const; | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ std::vector<unsigned char> GCodePreviewData::Color::as_bytes() const | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| GCodePreviewData::Extrusion::Layer::Layer(float z, const ExtrusionPaths& paths) | ||||
| GCodePreviewData::Extrusion::Layer::Layer(float z, const Paths& paths) | ||||
|     : z(z) | ||||
|     , paths(paths) | ||||
| { | ||||
|  | @ -171,8 +171,8 @@ size_t GCodePreviewData::Extrusion::memory_used() const | |||
|     size_t out = sizeof(*this); | ||||
|     out += SLIC3R_STDVEC_MEMSIZE(this->layers, Layer); | ||||
|     for (const Layer &layer : this->layers) { | ||||
|         out += SLIC3R_STDVEC_MEMSIZE(layer.paths, ExtrusionPath); | ||||
|         for (const ExtrusionPath &path : layer.paths) | ||||
|         out += SLIC3R_STDVEC_MEMSIZE(layer.paths, Path); | ||||
|         for (const Path &path : layer.paths) | ||||
| 			out += SLIC3R_STDVEC_MEMSIZE(path.polyline.points, Point); | ||||
|     } | ||||
| 	return out; | ||||
|  | @ -241,6 +241,7 @@ void GCodePreviewData::set_default() | |||
|     ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); | ||||
|     ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); | ||||
|     ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); | ||||
|     ::memcpy((void*)ranges.fan_speed.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); | ||||
|     ::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); | ||||
| 
 | ||||
|     extrusion.set_default(); | ||||
|  | @ -287,6 +288,11 @@ GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) con | |||
|     return ranges.feedrate.get_color_at(feedrate); | ||||
| } | ||||
| 
 | ||||
| GCodePreviewData::Color GCodePreviewData::get_fan_speed_color(float fan_speed) const | ||||
| { | ||||
|     return ranges.fan_speed.get_color_at(fan_speed); | ||||
| } | ||||
| 
 | ||||
| GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const | ||||
| { | ||||
|     return ranges.volumetric_rate.get_color_at(rate); | ||||
|  | @ -358,6 +364,8 @@ std::string GCodePreviewData::get_legend_title() const | |||
|         return L("Width (mm)"); | ||||
|     case Extrusion::Feedrate: | ||||
|         return L("Speed (mm/s)"); | ||||
|     case Extrusion::FanSpeed: | ||||
|         return L("Fan Speed (%)"); | ||||
|     case Extrusion::VolumetricRate: | ||||
|         return L("Volumetric flow rate (mm³/s)"); | ||||
|     case Extrusion::Tool: | ||||
|  | @ -421,6 +429,11 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: | |||
|             Helper::FillListFromRange(items, ranges.feedrate, 1, 1.0f); | ||||
|             break; | ||||
|         } | ||||
|     case Extrusion::FanSpeed: | ||||
|         { | ||||
|             Helper::FillListFromRange(items, ranges.fan_speed, 0, 1.0f); | ||||
|             break; | ||||
|         } | ||||
|     case Extrusion::VolumetricRate: | ||||
|         { | ||||
|             Helper::FillListFromRange(items, ranges.volumetric_rate, 3, 1.0f); | ||||
|  |  | |||
|  | @ -52,6 +52,8 @@ public: | |||
|         Range width; | ||||
|         // Color mapping by feedrate.
 | ||||
|         Range feedrate; | ||||
|         // Color mapping by fan speed.
 | ||||
|         Range fan_speed; | ||||
|         // Color mapping by volumetric extrusion rate.
 | ||||
|         Range volumetric_rate; | ||||
|     }; | ||||
|  | @ -74,6 +76,7 @@ public: | |||
|             Height, | ||||
|             Width, | ||||
|             Feedrate, | ||||
|             FanSpeed, | ||||
|             VolumetricRate, | ||||
|             Tool, | ||||
|             ColorPrint, | ||||
|  | @ -84,12 +87,34 @@ public: | |||
|         static const std::string Default_Extrusion_Role_Names[erCount]; | ||||
|         static const EViewType Default_View_Type; | ||||
| 
 | ||||
| 		class Path | ||||
| 		{ | ||||
| 		public: | ||||
| 		    Polyline 		polyline; | ||||
| 		    ExtrusionRole 	extrusion_role; | ||||
| 		    // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator.
 | ||||
| 		    float			mm3_per_mm; | ||||
| 		    // Width of the extrusion, used for visualization purposes.
 | ||||
| 		    float 			width; | ||||
| 		    // Height of the extrusion, used for visualization purposes.
 | ||||
| 		    float 			height; | ||||
| 		    // Feedrate of the extrusion, used for visualization purposes.
 | ||||
| 		    float 			feedrate; | ||||
| 		    // Id of the extruder, used for visualization purposes.
 | ||||
| 		    uint32_t		extruder_id; | ||||
| 		    // Id of the color, used for visualization purposes in the color printing case.
 | ||||
| 		    uint32_t	 	cp_color_id; | ||||
| 		    // Fan speed for the extrusion, used for visualization purposes.
 | ||||
| 		    float 			fan_speed; | ||||
| 		}; | ||||
| 		using Paths = std::vector<Path>; | ||||
| 
 | ||||
|         struct Layer | ||||
|         { | ||||
|             float z; | ||||
|             ExtrusionPaths paths; | ||||
|             Paths paths; | ||||
| 
 | ||||
|             Layer(float z, const ExtrusionPaths& paths); | ||||
|             Layer(float z, const Paths& paths); | ||||
|         }; | ||||
| 
 | ||||
|         typedef std::vector<Layer> LayersList; | ||||
|  | @ -205,6 +230,7 @@ public: | |||
|     Color get_height_color(float height) const; | ||||
|     Color get_width_color(float width) const; | ||||
|     Color get_feedrate_color(float feedrate) const; | ||||
|     Color get_fan_speed_color(float fan_speed) const; | ||||
|     Color get_volumetric_rate_color(float rate) const; | ||||
| 
 | ||||
|     void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha); | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| #include "ClipperUtils.hpp" | ||||
| #include "ExPolygon.hpp" | ||||
| #include "Line.hpp" | ||||
| #include "PolylineCollection.hpp" | ||||
| #include "clipper.hpp" | ||||
| #include <algorithm> | ||||
| #include <cassert> | ||||
|  |  | |||
|  | @ -19,7 +19,10 @@ public: | |||
| 	static constexpr size_t NumDimensions = ANumDimensions; | ||||
| 	using					CoordinateFn  = ACoordinateFn; | ||||
| 	using					CoordType     = ACoordType; | ||||
| 	static constexpr size_t npos		  = size_t(-1); | ||||
|     // 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)); } | ||||
|  | @ -61,15 +64,17 @@ public: | |||
| 	{ | ||||
| 		CoordType dist = point_coord - this->coordinate(idx, dimension); | ||||
| 		return (dist * dist < search_radius + CoordType(EPSILON)) ? | ||||
| 			// The plane intersects a hypersphere centered at point_coord of search_radius.
 | ||||
| 			((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) : | ||||
| 			(dist < CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT); | ||||
| 			// The plane does not intersect the hypersphere.
 | ||||
| 			(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 | ||||
| 	{ | ||||
| 		return m_nodes.empty() ? npos : visit_recursive(0, 0, visitor); | ||||
|         visit_recursive(0, 0, visitor); | ||||
| 	} | ||||
| 
 | ||||
| 	CoordinateFn coordinate; | ||||
|  |  | |||
|  | @ -47,8 +47,8 @@ void Layer::make_slices() | |||
|         slices = union_ex(slices_p); | ||||
|     } | ||||
|      | ||||
|     this->slices.expolygons.clear(); | ||||
|     this->slices.expolygons.reserve(slices.size()); | ||||
|     this->slices.clear(); | ||||
|     this->slices.reserve(slices.size()); | ||||
|      | ||||
|     // prepare ordering points
 | ||||
|     Points ordering_points; | ||||
|  | @ -61,7 +61,7 @@ void Layer::make_slices() | |||
|      | ||||
|     // populate slices vector
 | ||||
|     for (size_t i : order) | ||||
|         this->slices.expolygons.push_back(std::move(slices[i])); | ||||
|         this->slices.push_back(std::move(slices[i])); | ||||
| } | ||||
| 
 | ||||
| // Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
 | ||||
|  | @ -70,7 +70,7 @@ void Layer::merge_slices() | |||
|     if (m_regions.size() == 1) { | ||||
|         // Optimization, also more robust. Don't merge classified pieces of layerm->slices,
 | ||||
|         // but use the non-split islands of a layer. For a single region print, these shall be equal.
 | ||||
|         m_regions.front()->slices.set(this->slices.expolygons, stInternal); | ||||
|         m_regions.front()->slices.set(this->slices, stInternal); | ||||
|     } else { | ||||
|         for (LayerRegion *layerm : m_regions) | ||||
|             // without safety offset, artifacts are generated (GH #2494)
 | ||||
|  |  | |||
|  | @ -6,8 +6,6 @@ | |||
| #include "SurfaceCollection.hpp" | ||||
| #include "ExtrusionEntityCollection.hpp" | ||||
| #include "ExPolygonCollection.hpp" | ||||
| #include "PolylineCollection.hpp" | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -48,7 +46,7 @@ public: | |||
|     Polygons                    bridged; | ||||
| 
 | ||||
|     // collection of polylines representing the unsupported bridge edges
 | ||||
|     PolylineCollection          unsupported_bridge_edges; | ||||
|     Polylines          			unsupported_bridge_edges; | ||||
| 
 | ||||
|     // ordered collection of extrusion paths/loops to build all perimeters
 | ||||
|     // (this collection contains only ExtrusionEntityCollection objects)
 | ||||
|  | @ -112,7 +110,8 @@ public: | |||
|     // also known as 'islands' (all regions and surface types are merged here)
 | ||||
|     // The slices are chained by the shortest traverse distance and this traversal
 | ||||
|     // order will be recovered by the G-code generator.
 | ||||
|     ExPolygonCollection slices; | ||||
|     ExPolygons 			slices; | ||||
|     std::vector<BoundingBox> slices_bboxes; | ||||
| 
 | ||||
|     size_t                  region_count() const { return m_regions.size(); } | ||||
|     const LayerRegion*      get_region(int idx) const { return m_regions.at(idx); } | ||||
|  |  | |||
|  | @ -140,7 +140,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly | |||
|         	// Remove voids from fill_boundaries, that are not supported by the layer below.
 | ||||
|             if (lower_layer_covered == nullptr) { | ||||
|             	lower_layer_covered = &lower_layer_covered_tmp; | ||||
|             	lower_layer_covered_tmp = to_polygons(lower_layer->slices.expolygons); | ||||
|             	lower_layer_covered_tmp = to_polygons(lower_layer->slices); | ||||
|             } | ||||
|             if (! lower_layer_covered->empty()) | ||||
|             	voids = diff(voids, *lower_layer_covered); | ||||
|  | @ -272,7 +272,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly | |||
|                     bridges[idx_last].bridge_angle = bd.angle; | ||||
|                     if (this->layer()->object()->config().support_material) { | ||||
|                         polygons_append(this->bridged, bd.coverage()); | ||||
|                         this->unsupported_bridge_edges.append(bd.unsupported_edges());  | ||||
|                         append(this->unsupported_bridge_edges, bd.unsupported_edges()); | ||||
|                     } | ||||
| 				} else if (custom_angle > 0) { | ||||
| 					// Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in
 | ||||
|  |  | |||
|  | @ -3,11 +3,6 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| MultiPoint::operator Points() const | ||||
| { | ||||
|     return this->points; | ||||
| } | ||||
| 
 | ||||
| void MultiPoint::scale(double factor) | ||||
| { | ||||
|     for (Point &pt : points) | ||||
|  | @ -57,18 +52,7 @@ void MultiPoint::rotate(double angle, const Point ¢er) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void MultiPoint::reverse() | ||||
| { | ||||
|     std::reverse(this->points.begin(), this->points.end()); | ||||
| } | ||||
| 
 | ||||
| Point MultiPoint::first_point() const | ||||
| { | ||||
|     return this->points.front(); | ||||
| } | ||||
| 
 | ||||
| double | ||||
| MultiPoint::length() const | ||||
| double MultiPoint::length() const | ||||
| { | ||||
|     Lines lines = this->lines(); | ||||
|     double len = 0; | ||||
|  | @ -78,8 +62,7 @@ MultiPoint::length() const | |||
|     return len; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| MultiPoint::find_point(const Point &point) const | ||||
| int MultiPoint::find_point(const Point &point) const | ||||
| { | ||||
|     for (const Point &pt : this->points) | ||||
|         if (pt == point) | ||||
|  | @ -87,21 +70,18 @@ MultiPoint::find_point(const Point &point) const | |||
|     return -1;  // not found
 | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| MultiPoint::has_boundary_point(const Point &point) const | ||||
| bool MultiPoint::has_boundary_point(const Point &point) const | ||||
| { | ||||
|     double dist = (point.projection_onto(*this) - point).cast<double>().norm(); | ||||
|     return dist < SCALED_EPSILON; | ||||
| } | ||||
| 
 | ||||
| BoundingBox | ||||
| MultiPoint::bounding_box() const | ||||
| BoundingBox MultiPoint::bounding_box() const | ||||
| { | ||||
|     return BoundingBox(this->points); | ||||
| } | ||||
| 
 | ||||
| bool  | ||||
| MultiPoint::has_duplicate_points() const | ||||
| bool MultiPoint::has_duplicate_points() const | ||||
| { | ||||
|     for (size_t i = 1; i < points.size(); ++i) | ||||
|         if (points[i-1] == points[i]) | ||||
|  | @ -109,8 +89,7 @@ MultiPoint::has_duplicate_points() const | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| MultiPoint::remove_duplicate_points() | ||||
| bool MultiPoint::remove_duplicate_points() | ||||
| { | ||||
|     size_t j = 0; | ||||
|     for (size_t i = 1; i < points.size(); ++i) { | ||||
|  | @ -129,8 +108,7 @@ MultiPoint::remove_duplicate_points() | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| MultiPoint::intersection(const Line& line, Point* intersection) const | ||||
| bool MultiPoint::intersection(const Line& line, Point* intersection) const | ||||
| { | ||||
|     Lines lines = this->lines(); | ||||
|     for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) { | ||||
|  |  | |||
|  | @ -17,7 +17,8 @@ class MultiPoint | |||
| public: | ||||
|     Points points; | ||||
|      | ||||
|     operator Points() const; | ||||
|     operator Points() const { return this->points; } | ||||
| 
 | ||||
|     MultiPoint() {} | ||||
|     MultiPoint(const MultiPoint &other) : points(other.points) {} | ||||
|     MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {} | ||||
|  | @ -32,9 +33,10 @@ public: | |||
|     void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } | ||||
|     void rotate(double cos_angle, double sin_angle); | ||||
|     void rotate(double angle, const Point ¢er); | ||||
|     void reverse(); | ||||
|     Point first_point() const; | ||||
|     virtual Point last_point() const = 0; | ||||
|     void reverse() { std::reverse(this->points.begin(), this->points.end()); } | ||||
| 
 | ||||
|     const Point& first_point() const { return this->points.front(); } | ||||
|     virtual const Point& last_point() const = 0; | ||||
|     virtual Lines lines() const = 0; | ||||
|     size_t size() const { return points.size(); } | ||||
|     bool   empty() const { return points.empty(); } | ||||
|  |  | |||
|  | @ -175,10 +175,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime | |||
|                 perimeter_generator.overhang_flow.width, | ||||
|                 perimeter_generator.overhang_flow.height); | ||||
|              | ||||
|             // reapply the nearest point search for starting point
 | ||||
|             // We allow polyline reversal because Clipper may have randomly
 | ||||
|             // reversed polylines during clipping.
 | ||||
|             paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path(); | ||||
|             // Reapply the nearest point search for starting point.
 | ||||
|             // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
 | ||||
|             chain_and_reorder_extrusion_paths(paths, &paths.front().first_point()); | ||||
|         } else { | ||||
|             ExtrusionPath path(role); | ||||
|             path.polyline   = loop.polygon.split_at_first_point(); | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| 
 | ||||
| #include "libslic3r.h" | ||||
| #include <vector> | ||||
| #include "ExPolygonCollection.hpp" | ||||
| #include "Flow.hpp" | ||||
| #include "Polygon.hpp" | ||||
| #include "PrintConfig.hpp" | ||||
|  | @ -15,7 +14,7 @@ class PerimeterGenerator { | |||
| public: | ||||
|     // Inputs:
 | ||||
|     const SurfaceCollection     *slices; | ||||
|     const ExPolygonCollection   *lower_slices; | ||||
|     const ExPolygons            *lower_slices; | ||||
|     double                       layer_height; | ||||
|     int                          layer_id; | ||||
|     Flow                         perimeter_flow; | ||||
|  | @ -45,7 +44,7 @@ public: | |||
|         ExtrusionEntityCollection*  gap_fill, | ||||
|         // Infills without the gap fills
 | ||||
|         SurfaceCollection*          fill_surfaces) | ||||
|         : slices(slices), lower_slices(NULL), layer_height(layer_height), | ||||
|         : slices(slices), lower_slices(nullptr), layer_height(layer_height), | ||||
|             layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), | ||||
|             overhang_flow(flow), solid_infill_flow(flow), | ||||
|             config(config), object_config(object_config), print_config(print_config), | ||||
|  |  | |||
|  | @ -5,43 +5,12 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| Polygon::operator Polygons() const | ||||
| { | ||||
|     Polygons pp; | ||||
|     pp.push_back(*this); | ||||
|     return pp; | ||||
| } | ||||
| 
 | ||||
| Polygon::operator Polyline() const | ||||
| { | ||||
|     return this->split_at_first_point(); | ||||
| } | ||||
| 
 | ||||
| Point& | ||||
| Polygon::operator[](Points::size_type idx) | ||||
| { | ||||
|     return this->points[idx]; | ||||
| } | ||||
| 
 | ||||
| const Point& | ||||
| Polygon::operator[](Points::size_type idx) const | ||||
| { | ||||
|     return this->points[idx]; | ||||
| } | ||||
| 
 | ||||
| Point | ||||
| Polygon::last_point() const | ||||
| { | ||||
|     return this->points.front();  // last point == first point for polygons
 | ||||
| } | ||||
| 
 | ||||
| Lines Polygon::lines() const | ||||
| { | ||||
|     return to_lines(*this); | ||||
| } | ||||
| 
 | ||||
| Polyline | ||||
| Polygon::split_at_vertex(const Point &point) const | ||||
| Polyline Polygon::split_at_vertex(const Point &point) const | ||||
| { | ||||
|     // find index of point
 | ||||
|     for (const Point &pt : this->points) | ||||
|  | @ -52,8 +21,7 @@ Polygon::split_at_vertex(const Point &point) const | |||
| } | ||||
| 
 | ||||
| // Split a closed polygon into an open polyline, with the split point duplicated at both ends.
 | ||||
| Polyline | ||||
| Polygon::split_at_index(int index) const | ||||
| Polyline Polygon::split_at_index(int index) const | ||||
| { | ||||
|     Polyline polyline; | ||||
|     polyline.points.reserve(this->points.size() + 1); | ||||
|  | @ -64,19 +32,6 @@ Polygon::split_at_index(int index) const | |||
|     return polyline; | ||||
| } | ||||
| 
 | ||||
| // Split a closed polygon into an open polyline, with the split point duplicated at both ends.
 | ||||
| Polyline | ||||
| Polygon::split_at_first_point() const | ||||
| { | ||||
|     return this->split_at_index(0); | ||||
| } | ||||
| 
 | ||||
| Points | ||||
| Polygon::equally_spaced_points(double distance) const | ||||
| { | ||||
|     return this->split_at_first_point().equally_spaced_points(distance); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| int64_t Polygon::area2x() const | ||||
| { | ||||
|  | @ -107,20 +62,17 @@ double Polygon::area() const | |||
|     return 0.5 * a; | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| Polygon::is_counter_clockwise() const | ||||
| bool Polygon::is_counter_clockwise() const | ||||
| { | ||||
|     return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| Polygon::is_clockwise() const | ||||
| bool Polygon::is_clockwise() const | ||||
| { | ||||
|     return !this->is_counter_clockwise(); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| Polygon::make_counter_clockwise() | ||||
| bool Polygon::make_counter_clockwise() | ||||
| { | ||||
|     if (!this->is_counter_clockwise()) { | ||||
|         this->reverse(); | ||||
|  | @ -129,8 +81,7 @@ Polygon::make_counter_clockwise() | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| Polygon::make_clockwise() | ||||
| bool Polygon::make_clockwise() | ||||
| { | ||||
|     if (this->is_counter_clockwise()) { | ||||
|         this->reverse(); | ||||
|  | @ -139,16 +90,9 @@ Polygon::make_clockwise() | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| Polygon::is_valid() const | ||||
| { | ||||
|     return this->points.size() >= 3; | ||||
| } | ||||
| 
 | ||||
| // Does an unoriented polygon contain a point?
 | ||||
| // Tested by counting intersections along a horizontal line.
 | ||||
| bool | ||||
| Polygon::contains(const Point &point) const | ||||
| bool Polygon::contains(const Point &point) const | ||||
| { | ||||
|     // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
 | ||||
|     bool result = false; | ||||
|  | @ -174,8 +118,7 @@ Polygon::contains(const Point &point) const | |||
| } | ||||
| 
 | ||||
| // this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons()
 | ||||
| Polygons | ||||
| Polygon::simplify(double tolerance) const | ||||
| Polygons Polygon::simplify(double tolerance) const | ||||
| { | ||||
|     // repeat first point at the end in order to apply Douglas-Peucker
 | ||||
|     // on the whole polygon
 | ||||
|  | @ -189,8 +132,7 @@ Polygon::simplify(double tolerance) const | |||
|     return simplify_polygons(pp); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| Polygon::simplify(double tolerance, Polygons &polygons) const | ||||
| void Polygon::simplify(double tolerance, Polygons &polygons) const | ||||
| { | ||||
|     Polygons pp = this->simplify(tolerance); | ||||
|     polygons.reserve(polygons.size() + pp.size()); | ||||
|  | @ -198,8 +140,7 @@ Polygon::simplify(double tolerance, Polygons &polygons) const | |||
| } | ||||
| 
 | ||||
| // Only call this on convex polygons or it will return invalid results
 | ||||
| void | ||||
| Polygon::triangulate_convex(Polygons* polygons) const | ||||
| void Polygon::triangulate_convex(Polygons* polygons) const | ||||
| { | ||||
|     for (Points::const_iterator it = this->points.begin() + 2; it != this->points.end(); ++it) { | ||||
|         Polygon p; | ||||
|  | @ -214,8 +155,7 @@ Polygon::triangulate_convex(Polygons* polygons) const | |||
| } | ||||
| 
 | ||||
| // center of mass
 | ||||
| Point | ||||
| Polygon::centroid() const | ||||
| Point Polygon::centroid() const | ||||
| { | ||||
|     double area_temp = this->area(); | ||||
|     double x_temp = 0; | ||||
|  | @ -232,8 +172,7 @@ Polygon::centroid() const | |||
| 
 | ||||
| // find all concave vertices (i.e. having an internal angle greater than the supplied angle)
 | ||||
| // (external = right side, thus we consider ccw orientation)
 | ||||
| Points | ||||
| Polygon::concave_points(double angle) const | ||||
| Points Polygon::concave_points(double angle) const | ||||
| { | ||||
|     Points points; | ||||
|     angle = 2*PI - angle; | ||||
|  | @ -256,8 +195,7 @@ Polygon::concave_points(double angle) const | |||
| 
 | ||||
| // find all convex vertices (i.e. having an internal angle smaller than the supplied angle)
 | ||||
| // (external = right side, thus we consider ccw orientation)
 | ||||
| Points | ||||
| Polygon::convex_points(double angle) const | ||||
| Points Polygon::convex_points(double angle) const | ||||
| { | ||||
|     Points points; | ||||
|     angle = 2*PI - angle; | ||||
|  |  | |||
|  | @ -13,13 +13,14 @@ namespace Slic3r { | |||
| class Polygon; | ||||
| typedef std::vector<Polygon> Polygons; | ||||
| 
 | ||||
| class Polygon : public MultiPoint { | ||||
| class Polygon : public MultiPoint | ||||
| { | ||||
| public: | ||||
|     operator Polygons() const; | ||||
|     operator Polyline() const; | ||||
|     Point& operator[](Points::size_type idx); | ||||
|     const Point& operator[](Points::size_type idx) const; | ||||
|      | ||||
|     operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; } | ||||
|     operator Polyline() const { return this->split_at_first_point(); } | ||||
|     Point& operator[](Points::size_type idx) { return this->points[idx]; } | ||||
|     const Point& operator[](Points::size_type idx) const { return this->points[idx]; } | ||||
| 
 | ||||
|     Polygon() {} | ||||
|     explicit Polygon(const Points &points): MultiPoint(points) {} | ||||
|     Polygon(const Polygon &other) : MultiPoint(other.points) {} | ||||
|  | @ -34,20 +35,24 @@ public: | |||
|     Polygon& operator=(const Polygon &other) { points = other.points; return *this; } | ||||
|     Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; } | ||||
| 
 | ||||
|     Point last_point() const; | ||||
|     // last point == first point for polygons
 | ||||
|     const Point& last_point() const override { return this->points.front(); } | ||||
| 
 | ||||
|     virtual Lines lines() const; | ||||
|     Polyline split_at_vertex(const Point &point) const; | ||||
|     // Split a closed polygon into an open polyline, with the split point duplicated at both ends.
 | ||||
|     Polyline split_at_index(int index) const; | ||||
|     // Split a closed polygon into an open polyline, with the split point duplicated at both ends.
 | ||||
|     Polyline split_at_first_point() const; | ||||
|     Points equally_spaced_points(double distance) const; | ||||
|     Polyline split_at_first_point() const { return this->split_at_index(0); } | ||||
|     Points   equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); } | ||||
| 
 | ||||
|     double area() const; | ||||
|     bool is_counter_clockwise() const; | ||||
|     bool is_clockwise() const; | ||||
|     bool make_counter_clockwise(); | ||||
|     bool make_clockwise(); | ||||
|     bool is_valid() const; | ||||
|     bool is_valid() const { return this->points.size() >= 3; } | ||||
| 
 | ||||
|     // Does an unoriented polygon contain a point?
 | ||||
|     // Tested by counting intersections along a horizontal line.
 | ||||
|     bool contains(const Point &point) const; | ||||
|  |  | |||
|  | @ -23,18 +23,17 @@ Polyline::operator Line() const | |||
|     return Line(this->points.front(), this->points.back()); | ||||
| } | ||||
| 
 | ||||
| Point | ||||
| Polyline::leftmost_point() const | ||||
| const Point& Polyline::leftmost_point() const | ||||
| { | ||||
|     Point p = this->points.front(); | ||||
|     for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) { | ||||
|         if ((*it)(0) < p(0)) p = *it; | ||||
|     const Point *p = &this->points.front(); | ||||
|     for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++ it) { | ||||
|         if (it->x() < p->x())  | ||||
|         	p = &(*it); | ||||
|     } | ||||
|     return p; | ||||
|     return *p; | ||||
| } | ||||
| 
 | ||||
| Lines | ||||
| Polyline::lines() const | ||||
| Lines Polyline::lines() const | ||||
| { | ||||
|     Lines lines; | ||||
|     if (this->points.size() >= 2) { | ||||
|  | @ -205,6 +204,20 @@ BoundingBox get_extents(const Polylines &polylines) | |||
|     return bb; | ||||
| } | ||||
| 
 | ||||
| const Point& leftmost_point(const Polylines &polylines) | ||||
| { | ||||
|     if (polylines.empty()) | ||||
|         throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); | ||||
|     Polylines::const_iterator it = polylines.begin(); | ||||
|     const Point *p = &it->leftmost_point(); | ||||
|     for (++ it; it != polylines.end(); ++it) { | ||||
|         const Point *p2 = &it->leftmost_point(); | ||||
|         if (p2->x() < p->x()) | ||||
|             p = p2; | ||||
|     } | ||||
|     return *p; | ||||
| } | ||||
| 
 | ||||
| bool remove_degenerate(Polylines &polylines) | ||||
| { | ||||
|     bool modified = false; | ||||
|  |  | |||
|  | @ -62,9 +62,9 @@ public: | |||
| 
 | ||||
|     operator Polylines() const; | ||||
|     operator Line() const; | ||||
|     Point    last_point() const override { return this->points.back(); } | ||||
|     const Point& last_point() const override { return this->points.back(); } | ||||
| 
 | ||||
|     Point leftmost_point() const; | ||||
|     const Point& leftmost_point() const; | ||||
|     virtual Lines lines() const; | ||||
|     void clip_end(double distance); | ||||
|     void clip_start(double distance); | ||||
|  | @ -77,6 +77,15 @@ public: | |||
|     bool is_straight() const; | ||||
| }; | ||||
| 
 | ||||
| // Don't use this class in production code, it is used exclusively by the Perl binding for unit tests!
 | ||||
| #ifdef PERL_UCHAR_MIN | ||||
| class PolylineCollection | ||||
| { | ||||
| public: | ||||
|     Polylines polylines; | ||||
| }; | ||||
| #endif /* PERL_UCHAR_MIN */ | ||||
| 
 | ||||
| extern BoundingBox get_extents(const Polyline &polyline); | ||||
| extern BoundingBox get_extents(const Polylines &polylines); | ||||
| 
 | ||||
|  | @ -129,6 +138,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| const Point& leftmost_point(const Polylines &polylines); | ||||
| 
 | ||||
| bool remove_degenerate(Polylines &polylines); | ||||
| 
 | ||||
| class ThickPolyline : public Polyline { | ||||
|  |  | |||
|  | @ -1,92 +0,0 @@ | |||
| #include "PolylineCollection.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| struct Chaining | ||||
| { | ||||
|     Point first; | ||||
|     Point last; | ||||
|     size_t idx; | ||||
| }; | ||||
| 
 | ||||
| template<typename T> | ||||
| inline int nearest_point_index(const std::vector<Chaining> &pairs, const Point &start_near, bool no_reverse) | ||||
| { | ||||
|     T dmin = std::numeric_limits<T>::max(); | ||||
|     int idx = 0; | ||||
|     for (std::vector<Chaining>::const_iterator it = pairs.begin(); it != pairs.end(); ++it) { | ||||
|         T d = sqr(T(start_near(0) - it->first(0))); | ||||
|         if (d <= dmin) { | ||||
|             d += sqr(T(start_near(1) - it->first(1))); | ||||
|             if (d < dmin) { | ||||
|                 idx = (it - pairs.begin()) * 2; | ||||
|                 dmin = d; | ||||
|                 if (dmin < EPSILON) | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         if (! no_reverse) { | ||||
|             d = sqr(T(start_near(0) - it->last(0))); | ||||
|             if (d <= dmin) { | ||||
|                 d += sqr(T(start_near(1) - it->last(1))); | ||||
|                 if (d < dmin) { | ||||
|                     idx = (it - pairs.begin()) * 2 + 1; | ||||
|                     dmin = d; | ||||
|                     if (dmin < EPSILON) | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return idx; | ||||
| } | ||||
| 
 | ||||
| Polylines PolylineCollection::_chained_path_from( | ||||
|     const Polylines &src, | ||||
|     Point start_near, | ||||
|     bool  no_reverse,  | ||||
|     bool  move_from_src) | ||||
| { | ||||
|     std::vector<Chaining> endpoints; | ||||
|     endpoints.reserve(src.size()); | ||||
|     for (size_t i = 0; i < src.size(); ++ i) { | ||||
|         Chaining c; | ||||
|         c.first = src[i].first_point(); | ||||
|         if (! no_reverse) | ||||
|             c.last = src[i].last_point(); | ||||
|         c.idx = i; | ||||
|         endpoints.push_back(c); | ||||
|     } | ||||
|     Polylines retval; | ||||
|     while (! endpoints.empty()) { | ||||
|         // find nearest point
 | ||||
|         int endpoint_index = nearest_point_index<double>(endpoints, start_near, no_reverse); | ||||
|         assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2); | ||||
|         if (move_from_src) { | ||||
|             retval.push_back(std::move(src[endpoints[endpoint_index/2].idx])); | ||||
|         } else { | ||||
|             retval.push_back(src[endpoints[endpoint_index/2].idx]); | ||||
|         } | ||||
|         if (endpoint_index & 1) | ||||
|             retval.back().reverse(); | ||||
|         endpoints.erase(endpoints.begin() + endpoint_index/2); | ||||
|         start_near = retval.back().last_point(); | ||||
|     } | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
| Point PolylineCollection::leftmost_point(const Polylines &polylines) | ||||
| { | ||||
|     if (polylines.empty()) | ||||
|         throw std::invalid_argument("leftmost_point() called on empty PolylineCollection"); | ||||
|     Polylines::const_iterator it = polylines.begin(); | ||||
|     Point p = it->leftmost_point(); | ||||
|     for (++ it; it != polylines.end(); ++it) { | ||||
|         Point p2 = it->leftmost_point(); | ||||
|         if (p2(0) < p(0)) | ||||
|             p = p2; | ||||
|     } | ||||
|     return p; | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  | @ -1,47 +0,0 @@ | |||
| #ifndef slic3r_PolylineCollection_hpp_ | ||||
| #define slic3r_PolylineCollection_hpp_ | ||||
| 
 | ||||
| #include "libslic3r.h" | ||||
| #include "Polyline.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| class PolylineCollection | ||||
| { | ||||
|     static Polylines _chained_path_from( | ||||
|         const Polylines &src, | ||||
|         Point start_near, | ||||
|         bool no_reverse,  | ||||
|         bool move_from_src); | ||||
| 
 | ||||
| public: | ||||
|     Polylines polylines; | ||||
|     void chained_path(PolylineCollection* retval, bool no_reverse = false) const | ||||
|     	{ retval->polylines = chained_path(this->polylines, no_reverse); } | ||||
|     void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const | ||||
|     	{ retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); } | ||||
|     Point leftmost_point() const | ||||
|     	{ return leftmost_point(polylines); } | ||||
|     void append(const Polylines &polylines) | ||||
|         { this->polylines.insert(this->polylines.end(), polylines.begin(), polylines.end()); } | ||||
| 
 | ||||
| 	static Point     leftmost_point(const Polylines &polylines); | ||||
| 	static Polylines chained_path(Polylines &&src, bool no_reverse = false) { | ||||
|         return (src.empty() || src.front().points.empty()) ? | ||||
|             Polylines() : | ||||
|             _chained_path_from(src, src.front().first_point(), no_reverse, true); | ||||
|     } | ||||
| 	static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false) | ||||
|         { return _chained_path_from(src, start_near, no_reverse, true); } | ||||
|     static Polylines chained_path(const Polylines &src, bool no_reverse = false) { | ||||
|         return (src.empty() || src.front().points.empty()) ? | ||||
|             Polylines() : | ||||
|             _chained_path_from(src, src.front().first_point(), no_reverse, false); | ||||
|     } | ||||
|     static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false) | ||||
|         { return _chained_path_from(src, start_near, no_reverse, false); } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -328,17 +328,6 @@ unsigned int Print::num_object_instances() const | |||
|     return instances; | ||||
| } | ||||
| 
 | ||||
| void Print::_simplify_slices(double distance) | ||||
| { | ||||
|     for (PrintObject *object : m_objects) { | ||||
|         for (Layer *layer : object->m_layers) { | ||||
|             layer->slices.simplify(distance); | ||||
|             for (LayerRegion *layerm : layer->regions()) | ||||
|                 layerm->slices.simplify(distance); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| double Print::max_allowed_layer_height() const | ||||
| { | ||||
|     double nozzle_diameter_max = 0.; | ||||
|  | @ -1114,6 +1103,9 @@ std::string Print::validate() const | |||
|     if (m_objects.empty()) | ||||
|         return L("All objects are outside of the print volume."); | ||||
| 
 | ||||
|     if (extruders().empty()) | ||||
|         return L("The supplied settings will cause an empty print."); | ||||
| 
 | ||||
|     if (m_config.complete_objects) { | ||||
|         // Check horizontal clearance.
 | ||||
|         { | ||||
|  | @ -1271,10 +1263,7 @@ std::string Print::validate() const | |||
|     } | ||||
|      | ||||
| 	{ | ||||
| 		// find the smallest nozzle diameter
 | ||||
| 		std::vector<unsigned int> extruders = this->extruders(); | ||||
| 		if (extruders.empty()) | ||||
| 			return L("The supplied settings will cause an empty print."); | ||||
| 
 | ||||
| 		// Find the smallest used nozzle diameter and the number of unique nozzle diameters.
 | ||||
| 		double min_nozzle_diameter = std::numeric_limits<double>::max(); | ||||
|  | @ -1593,7 +1582,7 @@ void Print::_make_skirt() | |||
|         for (const Layer *layer : object->m_layers) { | ||||
|             if (layer->print_z > skirt_height_z) | ||||
|                 break; | ||||
|             for (const ExPolygon &expoly : layer->slices.expolygons) | ||||
|             for (const ExPolygon &expoly : layer->slices) | ||||
|                 // Collect the outer contour points only, ignore holes for the calculation of the convex hull.
 | ||||
|                 append(object_points, expoly.contour.points); | ||||
|         } | ||||
|  | @ -1704,7 +1693,7 @@ void Print::_make_brim() | |||
|     Polygons    islands; | ||||
|     for (PrintObject *object : m_objects) { | ||||
|         Polygons object_islands; | ||||
|         for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons) | ||||
|         for (ExPolygon &expoly : object->m_layers.front()->slices) | ||||
|             object_islands.push_back(expoly.contour); | ||||
|         if (! object->support_layers().empty()) | ||||
|             object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); | ||||
|  |  | |||
|  | @ -180,7 +180,6 @@ private: | |||
|     void _slice(const std::vector<coordf_t> &layer_height_profile); | ||||
|     std::string _fix_slicing_errors(); | ||||
|     void _simplify_slices(double distance); | ||||
|     void _make_perimeters(); | ||||
|     bool has_support_material() const; | ||||
|     void detect_surfaces_type(); | ||||
|     void process_external_surfaces(); | ||||
|  | @ -383,7 +382,6 @@ private: | |||
|     void                _make_skirt(); | ||||
|     void                _make_brim(); | ||||
|     void                _make_wipe_tower(); | ||||
|     void                _simplify_slices(double distance); | ||||
| 
 | ||||
|     // Declared here to have access to Model / ModelObject / ModelInstance
 | ||||
|     static void         model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src); | ||||
|  |  | |||
|  | @ -2392,6 +2392,7 @@ void PrintConfigDef::init_sla_params() | |||
|                       "the threshold in the middle. This behaviour eliminates " | ||||
|                       "antialiasing without losing holes in polygons."); | ||||
|     def->min = 0; | ||||
|     def->max = 1; | ||||
|     def->mode = comExpert; | ||||
|     def->set_default_value(new ConfigOptionFloat(1.0)); | ||||
| 
 | ||||
|  |  | |||
|  | @ -117,6 +117,19 @@ void PrintObject::slice() | |||
|     // Simplify slices if required.
 | ||||
|     if (m_print->config().resolution) | ||||
|         this->_simplify_slices(scale_(this->print()->config().resolution)); | ||||
|     // Update bounding boxes
 | ||||
|     tbb::parallel_for( | ||||
|         tbb::blocked_range<size_t>(0, m_layers.size()), | ||||
|         [this](const tbb::blocked_range<size_t>& range) { | ||||
|             for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { | ||||
|                 m_print->throw_if_canceled(); | ||||
|                 Layer &layer = *m_layers[layer_idx]; | ||||
|                 layer.slices_bboxes.clear(); | ||||
|                 layer.slices_bboxes.reserve(layer.slices.size()); | ||||
|                 for (const ExPolygon &expoly : layer.slices) | ||||
|                 	layer.slices_bboxes.emplace_back(get_extents(expoly)); | ||||
|             } | ||||
|         }); | ||||
|     if (m_layers.empty()) | ||||
|         throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");     | ||||
|     this->set_done(posSlice); | ||||
|  | @ -865,7 +878,7 @@ void PrintObject::process_external_surfaces() | |||
| 		                			// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
 | ||||
| 		                			polygons_append(voids, offset(surface.expolygon, unsupported_width)); | ||||
| 		                } | ||||
| 		                surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices.expolygons), voids); | ||||
| 		                surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices), voids); | ||||
| 	            	} | ||||
| 	        } | ||||
| 	    ); | ||||
|  | @ -975,8 +988,8 @@ void PrintObject::discover_vertical_shells() | |||
|                         polygons_append(cache.holes, offset(offset_ex(layer.slices, 0.3f * perimeter_min_spacing), - perimeter_offset - 0.3f * perimeter_min_spacing)); | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
|                         { | ||||
|                             Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices.expolygons)); | ||||
|                             svg.draw(layer.slices.expolygons, "blue"); | ||||
|                             Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices)); | ||||
|                             svg.draw(layer.slices, "blue"); | ||||
|                             svg.draw(union_ex(cache.holes), "red"); | ||||
|                             svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); | ||||
|                             svg.Close();  | ||||
|  | @ -1659,25 +1672,26 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile) | |||
|                     // Trim volumes in a single layer, one by the other, possibly apply upscaling.
 | ||||
|                     { | ||||
|                         Polygons processed; | ||||
|                         for (SlicedVolume &sliced_volume : sliced_volumes) { | ||||
|                             ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]); | ||||
|                             if (upscale) | ||||
|                                 slices = offset_ex(std::move(slices), delta); | ||||
|                             if (! processed.empty()) | ||||
|                                 // Trim by the slices of already processed regions.
 | ||||
|                                 slices = diff_ex(to_polygons(std::move(slices)), processed); | ||||
|                             if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size()) | ||||
|                                 // Collect the already processed regions to trim the to be processed regions.
 | ||||
|                                 polygons_append(processed, slices); | ||||
|                             sliced_volume.expolygons_by_layer[layer_id] = std::move(slices); | ||||
|                         } | ||||
|                         for (SlicedVolume &sliced_volume : sliced_volumes)  | ||||
|                         	if (! sliced_volume.expolygons_by_layer.empty()) { | ||||
| 	                            ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]); | ||||
| 	                            if (upscale) | ||||
| 	                                slices = offset_ex(std::move(slices), delta); | ||||
| 	                            if (! processed.empty()) | ||||
| 	                                // Trim by the slices of already processed regions.
 | ||||
| 	                                slices = diff_ex(to_polygons(std::move(slices)), processed); | ||||
| 	                            if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size()) | ||||
| 	                                // Collect the already processed regions to trim the to be processed regions.
 | ||||
| 	                                polygons_append(processed, slices); | ||||
| 	                            sliced_volume.expolygons_by_layer[layer_id] = std::move(slices); | ||||
| 	                        } | ||||
|                     } | ||||
|                     // Collect and union volumes of a single region.
 | ||||
|                     for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) { | ||||
|                         ExPolygons expolygons; | ||||
|                         size_t     num_volumes = 0; | ||||
|                         for (SlicedVolume &sliced_volume : sliced_volumes) | ||||
|                             if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer[layer_id].empty()) { | ||||
|                             if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer.empty() && ! sliced_volume.expolygons_by_layer[layer_id].empty()) { | ||||
|                                 ++ num_volumes; | ||||
|                                 append(expolygons, std::move(sliced_volume.expolygons_by_layer[layer_id])); | ||||
|                             } | ||||
|  | @ -2140,7 +2154,7 @@ std::string PrintObject::_fix_slicing_errors() | |||
|     BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end"; | ||||
| 
 | ||||
|     // remove empty layers from bottom
 | ||||
|     while (! m_layers.empty() && m_layers.front()->slices.expolygons.empty()) { | ||||
|     while (! m_layers.empty() && m_layers.front()->slices.empty()) { | ||||
|         delete m_layers.front(); | ||||
|         m_layers.erase(m_layers.begin()); | ||||
|         m_layers.front()->lower_layer = nullptr; | ||||
|  | @ -2167,115 +2181,17 @@ void PrintObject::_simplify_slices(double distance) | |||
|                 Layer *layer = m_layers[layer_idx]; | ||||
|                 for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx) | ||||
|                     layer->m_regions[region_idx]->slices.simplify(distance); | ||||
|                 layer->slices.simplify(distance); | ||||
| 				{ | ||||
| 					ExPolygons simplified; | ||||
| 					for (const ExPolygon& expoly : layer->slices) | ||||
| 						expoly.simplify(distance, &simplified); | ||||
| 					layer->slices = std::move(simplified); | ||||
| 				} | ||||
|             } | ||||
|         }); | ||||
|     BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end"; | ||||
| } | ||||
| 
 | ||||
| void PrintObject::_make_perimeters() | ||||
| { | ||||
|     if (! this->set_started(posPerimeters)) | ||||
|         return; | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); | ||||
|      | ||||
|     // merge slices if they were split into types
 | ||||
|     if (this->typed_slices) { | ||||
|         for (Layer *layer : m_layers) | ||||
|             layer->merge_slices(); | ||||
|         this->typed_slices = false; | ||||
|         this->invalidate_step(posPrepareInfill); | ||||
|     } | ||||
|      | ||||
|     // compare each layer to the one below, and mark those slices needing
 | ||||
|     // one additional inner perimeter, like the top of domed objects-
 | ||||
|      | ||||
|     // this algorithm makes sure that at least one perimeter is overlapping
 | ||||
|     // but we don't generate any extra perimeter if fill density is zero, as they would be floating
 | ||||
|     // inside the object - infill_only_where_needed should be the method of choice for printing
 | ||||
|     // hollow objects
 | ||||
|     for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { | ||||
|         const PrintRegion ®ion = *m_print->regions()[region_id]; | ||||
|         if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) | ||||
|             continue; | ||||
|          | ||||
|         BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start"; | ||||
|         tbb::parallel_for( | ||||
|             tbb::blocked_range<size_t>(0, m_layers.size() - 1), | ||||
|             [this, ®ion, region_id](const tbb::blocked_range<size_t>& range) { | ||||
|                 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { | ||||
|                     LayerRegion &layerm                     = *m_layers[layer_idx]->regions()[region_id]; | ||||
|                     const LayerRegion &upper_layerm         = *m_layers[layer_idx+1]->regions()[region_id]; | ||||
|                     const Polygons upper_layerm_polygons    = upper_layerm.slices; | ||||
|                     // Filter upper layer polygons in intersection_ppl by their bounding boxes?
 | ||||
|                     // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
 | ||||
|                     const double total_loop_length      = total_length(upper_layerm_polygons); | ||||
|                     const coord_t perimeter_spacing     = layerm.flow(frPerimeter).scaled_spacing(); | ||||
|                     const Flow ext_perimeter_flow       = layerm.flow(frExternalPerimeter); | ||||
|                     const coord_t ext_perimeter_width   = ext_perimeter_flow.scaled_width(); | ||||
|                     const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing(); | ||||
| 
 | ||||
|                     for (Surface &slice : layerm.slices.surfaces) { | ||||
|                         for (;;) { | ||||
|                             // compute the total thickness of perimeters
 | ||||
|                             const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 | ||||
|                                 + (region.config().perimeters-1 + slice.extra_perimeters) * perimeter_spacing; | ||||
|                             // define a critical area where we don't want the upper slice to fall into
 | ||||
|                             // (it should either lay over our perimeters or outside this area)
 | ||||
|                             const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5); | ||||
|                             const Polygons critical_area = diff( | ||||
|                                 offset(slice.expolygon, float(- perimeters_thickness)), | ||||
|                                 offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth)) | ||||
|                             ); | ||||
|                             // check whether a portion of the upper slices falls inside the critical area
 | ||||
|                             const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area); | ||||
|                             // only add an additional loop if at least 30% of the slice loop would benefit from it
 | ||||
|                             if (total_length(intersection) <=  total_loop_length*0.3) | ||||
|                                 break; | ||||
|                             /*
 | ||||
|                             if (0) { | ||||
|                                 require "Slic3r/SVG.pm"; | ||||
|                                 Slic3r::SVG::output( | ||||
|                                     "extra.svg", | ||||
|                                     no_arrows   => 1, | ||||
|                                     expolygons  => union_ex($critical_area), | ||||
|                                     polylines   => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], | ||||
|                                 ); | ||||
|                             } | ||||
|                             */ | ||||
|                             ++ slice.extra_perimeters; | ||||
|                         } | ||||
|                         #ifdef DEBUG | ||||
|                             if (slice.extra_perimeters > 0) | ||||
|                                 printf("  adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx); | ||||
|                         #endif | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|         BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end"; | ||||
|     } | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; | ||||
|     tbb::parallel_for( | ||||
|         tbb::blocked_range<size_t>(0, m_layers.size()), | ||||
|         [this](const tbb::blocked_range<size_t>& range) { | ||||
|             for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) | ||||
|                 m_layers[layer_idx]->make_perimeters(); | ||||
|         } | ||||
|     ); | ||||
|     BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; | ||||
| 
 | ||||
|     /*
 | ||||
|         simplify slices (both layer and region slices), | ||||
|         we only need the max resolution for perimeters | ||||
|     ### This makes this method not-idempotent, so we keep it disabled for now. | ||||
|     ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); | ||||
|     */ | ||||
|      | ||||
|     this->set_done(posPerimeters); | ||||
| } | ||||
| 
 | ||||
| // Only active if config->infill_only_where_needed. This step trims the sparse infill,
 | ||||
| // so it acts as an internal support. It maintains all other infill types intact.
 | ||||
| // Here the internal surfaces and perimeters have to be supported by the sparse infill.
 | ||||
|  | @ -2301,7 +2217,7 @@ void PrintObject::clip_fill_surfaces() | |||
|         // Detect things that we need to support.
 | ||||
|         // Cummulative slices.
 | ||||
|         Polygons slices; | ||||
|         polygons_append(slices, layer->slices.expolygons); | ||||
|         polygons_append(slices, layer->slices); | ||||
|         // Cummulative fill surfaces.
 | ||||
|         Polygons fill_surfaces; | ||||
|         // Solid surfaces to be supported.
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| 	#undef assert | ||||
| #endif | ||||
| 
 | ||||
| #include "clipper.hpp" | ||||
| #include "ShortestPath.hpp" | ||||
| #include "KDTreeIndirect.hpp" | ||||
| #include "MutablePriorityQueue.hpp" | ||||
|  | @ -14,6 +15,44 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| // Naive implementation of the Traveling Salesman Problem, it works by always taking the next closest neighbor.
 | ||||
| // This implementation will always produce valid result even if some segments cannot reverse.
 | ||||
| template<typename EndPointType, typename KDTreeType, typename CouldReverseFunc> | ||||
| std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<EndPointType> &end_points, KDTreeType &kdtree, CouldReverseFunc &could_reverse_func, EndPointType &first_point) | ||||
| { | ||||
| 	assert((end_points.size() & 1) == 0); | ||||
| 	size_t num_segments = end_points.size() / 2; | ||||
| 	assert(num_segments >= 2); | ||||
| 	for (EndPointType &ep : end_points) | ||||
| 		ep.chain_id = 0; | ||||
| 	std::vector<std::pair<size_t, bool>> out; | ||||
| 	out.reserve(num_segments); | ||||
| 	size_t first_point_idx = &first_point - end_points.data(); | ||||
| 	out.emplace_back(first_point_idx / 2, (first_point_idx & 1) != 0); | ||||
| 	first_point.chain_id = 1; | ||||
| 	size_t this_idx = first_point_idx ^ 1; | ||||
| 	for (int iter = (int)num_segments - 2; iter >= 0; -- iter) { | ||||
| 		EndPointType &this_point = end_points[this_idx]; | ||||
|     	this_point.chain_id = 1; | ||||
|     	// 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, this_point.pos, | ||||
| 			[this_idx, &end_points, &could_reverse_func](size_t idx) { | ||||
| 				return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1)); | ||||
| 		}); | ||||
| 		assert(next_idx < end_points.size()); | ||||
| 		EndPointType &end_point = end_points[next_idx]; | ||||
| 		end_point.chain_id = 1; | ||||
| 		this_idx = next_idx ^ 1; | ||||
| 	} | ||||
| #ifndef NDEBUG | ||||
| 	assert(end_points[this_idx].chain_id == 0); | ||||
| 	for (EndPointType &ep : end_points) | ||||
| 		assert(&ep == &end_points[this_idx] || ep.chain_id == 1); | ||||
| #endif /* NDEBUG */ | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| // 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.
 | ||||
|  | @ -22,8 +61,8 @@ namespace Slic3r { | |||
| // 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) | ||||
| template<typename PointType, typename SegmentEndPointFunc, bool REVERSE_COULD_FAIL, typename CouldReverseFunc> | ||||
| std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals_(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) | ||||
| { | ||||
| 	std::vector<std::pair<size_t, bool>> out; | ||||
| 
 | ||||
|  | @ -34,7 +73,7 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin | |||
| 	{ | ||||
| 		// 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).cast<double>().squaredNorm() < (end_point_func(0, false) - *start_near).cast<double>().squaredNorm()); | ||||
|             (end_point_func(0, true) - *start_near).template cast<double>().squaredNorm() < (end_point_func(0, false) - *start_near).template cast<double>().squaredNorm()); | ||||
| 	}  | ||||
| 	else | ||||
| 	{ | ||||
|  | @ -55,8 +94,8 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin | |||
| 	    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 ).cast<double>()); | ||||
| 	    	end_points.emplace_back(end_point_func(i, false).cast<double>()); | ||||
|             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.
 | ||||
|  | @ -125,13 +164,15 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin | |||
| 		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->cast<double>()); | ||||
|             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; | ||||
| 		} | ||||
| 		EndPoint *initial_point = first_point; | ||||
| 		EndPoint *last_point = nullptr; | ||||
| 
 | ||||
| 		// Assign the closest point and distance to the end points.
 | ||||
| 		for (EndPoint &end_point : end_points) { | ||||
|  | @ -240,12 +281,15 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin | |||
| 				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) | ||||
| 					if (first_point == nullptr) { | ||||
| 						first_point = queue.top(); | ||||
| 					while (! queue.empty()) { | ||||
| 						queue.top()->edge_out = nullptr; | ||||
| 						queue.pop(); | ||||
| 						first_point->edge_out = nullptr; | ||||
| 					} | ||||
| 					last_point = queue.top(); | ||||
| 					last_point->edge_out = nullptr; | ||||
| 					queue.pop(); | ||||
| 					assert(queue.empty()); | ||||
| 					break; | ||||
| 				} | ||||
| 	    	} else { | ||||
|  | @ -280,24 +324,77 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin | |||
| 
 | ||||
| 		// Now interconnect pairs of segments into a chain.
 | ||||
| 		assert(first_point != nullptr); | ||||
| 		out.reserve(num_segments); | ||||
| 		bool      failed = false; | ||||
| 		do { | ||||
| 			assert(out.size() < num_segments); | ||||
| 			size_t    		 first_point_id = first_point - &end_points.front(); | ||||
| 			size_t           segment_id 	= first_point_id >> 1; | ||||
| 			bool             reverse        = (first_point_id & 1) != 0; | ||||
| 			EndPoint 		*second_point   = &end_points[first_point_id ^ 1]; | ||||
| 			out.emplace_back(segment_id, (first_point_id & 1) != 0); | ||||
| 			if (REVERSE_COULD_FAIL) { | ||||
| 				if (reverse && ! could_reverse_func(segment_id)) { | ||||
| 					failed = true; | ||||
| 					break; | ||||
| 				} | ||||
| 			} else { | ||||
| 				assert(! reverse || could_reverse_func(segment_id)); | ||||
| 			} | ||||
| 			out.emplace_back(segment_id, reverse); | ||||
| 			first_point = second_point->edge_out; | ||||
| 		} while (first_point != nullptr); | ||||
| 		if (REVERSE_COULD_FAIL) { | ||||
| 			if (failed) { | ||||
| 				if (start_near == nullptr) { | ||||
| 					// We may try the reverse order.
 | ||||
| 					out.clear(); | ||||
| 					first_point = last_point; | ||||
| 					failed = false; | ||||
| 					do { | ||||
| 						assert(out.size() < num_segments); | ||||
| 						size_t    		 first_point_id = first_point - &end_points.front(); | ||||
| 						size_t           segment_id 	= first_point_id >> 1; | ||||
| 						bool             reverse        = (first_point_id & 1) != 0; | ||||
| 						EndPoint 		*second_point   = &end_points[first_point_id ^ 1]; | ||||
| 						if (reverse && ! could_reverse_func(segment_id)) { | ||||
| 							failed = true; | ||||
| 							break; | ||||
| 						} | ||||
| 						out.emplace_back(segment_id, reverse); | ||||
| 						first_point = second_point->edge_out; | ||||
| 					} while (first_point != nullptr); | ||||
| 				} | ||||
| 			} | ||||
| 			if (failed) | ||||
| 				// As a last resort, try a dumb algorithm, which is not sensitive to edge reversal constraints.
 | ||||
| 				out = chain_segments_closest_point<EndPoint, decltype(kdtree), CouldReverseFunc>(end_points, kdtree, could_reverse_func, (initial_point != nullptr) ? *initial_point : end_points.front()); | ||||
| 		} else { | ||||
| 			assert(! failed); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	assert(out.size() == num_segments); | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| template<typename PointType, typename SegmentEndPointFunc, typename CouldReverseFunc> | ||||
| std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near) | ||||
| { | ||||
| 	return chain_segments_greedy_constrained_reversals_<PointType, SegmentEndPointFunc, true, CouldReverseFunc>(end_point_func, could_reverse_func, num_segments, start_near); | ||||
| } | ||||
| 
 | ||||
| template<typename PointType, typename SegmentEndPointFunc> | ||||
| std::vector<std::pair<size_t, bool>> chain_segments_greedy(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near) | ||||
| { | ||||
| 	auto could_reverse_func = [](size_t /* idx */) -> bool { return true; }; | ||||
| 	return chain_segments_greedy_constrained_reversals_<PointType, SegmentEndPointFunc, false, decltype(could_reverse_func)>(end_point_func, could_reverse_func, num_segments, start_near); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 	auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; | ||||
| 	std::vector<std::pair<size_t, bool>> out = chain_segments_greedy_constrained_reversals<Point, decltype(segment_end_point), decltype(could_reverse)>(segment_end_point, could_reverse, entities.size(), start_near); | ||||
| 	for (size_t i = 0; i < entities.size(); ++ i) { | ||||
| 		ExtrusionEntity *ee = entities[i]; | ||||
| 		if (ee->is_loop()) | ||||
|  | @ -309,7 +406,7 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus | |||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain) | ||||
| 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; | ||||
|  | @ -328,10 +425,34 @@ void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entitie | |||
| 	reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near)); | ||||
| } | ||||
| 
 | ||||
| std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near) | ||||
| { | ||||
| 	auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); }; | ||||
| 	return chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, extrusion_paths.size(), start_near); | ||||
| } | ||||
| 
 | ||||
| void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const std::vector<std::pair<size_t, bool>> &chain) | ||||
| { | ||||
| 	assert(extrusion_paths.size() == chain.size()); | ||||
| 	std::vector<ExtrusionPath> out; | ||||
| 	out.reserve(extrusion_paths.size()); | ||||
|     for (const std::pair<size_t, bool> &idx : chain) { | ||||
|         out.emplace_back(std::move(extrusion_paths[idx.first])); | ||||
|         if (idx.second) | ||||
| 			out.back().reverse(); | ||||
|     } | ||||
|     extrusion_paths.swap(out); | ||||
| } | ||||
| 
 | ||||
| void chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near) | ||||
| { | ||||
| 	reorder_extrusion_paths(extrusion_paths, chain_extrusion_paths(extrusion_paths, 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<std::pair<size_t, bool>> ordered = chain_segments_greedy<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) | ||||
|  | @ -339,12 +460,12 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near) | |||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| Polylines chain_infill_polylines(Polylines &polylines) | ||||
| Polylines chain_polylines(Polylines &&polylines, const Point *start_near) | ||||
| { | ||||
| 	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); | ||||
| 	std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), start_near); | ||||
| 	Polylines out; | ||||
| 	out.reserve(polylines.size()); | ||||
| 	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) | ||||
|  | @ -356,7 +477,7 @@ Polylines chain_infill_polylines(Polylines &polylines) | |||
| 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); | ||||
| 	std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr); | ||||
| 	T out; | ||||
| 	out.reserve(items.size()); | ||||
| 	for (auto &segment_and_reversal : ordered) | ||||
|  | @ -382,7 +503,7 @@ std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print | |||
|         } | ||||
|     } | ||||
| 	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, bool>> ordered = chain_segments_greedy<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) | ||||
|  |  | |||
|  | @ -15,10 +15,15 @@ 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, std::vector<std::pair<size_t, bool>> &chain); | ||||
| 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<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr); | ||||
| void                                 reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, std::vector<std::pair<size_t, bool>> &chain); | ||||
| void                                 chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr); | ||||
| 
 | ||||
| Polylines 							 chain_polylines(Polylines &&src, const Point *start_near = nullptr); | ||||
| inline Polylines 					 chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); } | ||||
| 
 | ||||
| std::vector<ClipperLib::PolyNode*>	 chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items); | ||||
| 
 | ||||
|  |  | |||
|  | @ -445,8 +445,8 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t | |||
| Polygons collect_slices_outer(const Layer &layer) | ||||
| { | ||||
|     Polygons out; | ||||
|     out.reserve(out.size() + layer.slices.expolygons.size()); | ||||
|     for (const ExPolygon &expoly : layer.slices.expolygons) | ||||
|     out.reserve(out.size() + layer.slices.size()); | ||||
|     for (const ExPolygon &expoly : layer.slices) | ||||
|         out.emplace_back(expoly.contour); | ||||
|     return out; | ||||
| } | ||||
|  | @ -907,9 +907,13 @@ namespace SupportMaterialInternal { | |||
|                     polyline.extend_start(fw); | ||||
|                     polyline.extend_end(fw); | ||||
|                     // Is the straight perimeter segment supported at both sides?
 | ||||
|                     if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point())) | ||||
|                         // Offset a polyline into a thick line.
 | ||||
|                         polygons_append(bridges, offset(polyline, 0.5f * w + 10.f)); | ||||
| 					for (size_t i = 0; i < lower_layer.slices.size(); ++ i) | ||||
| 						if (lower_layer.slices_bboxes[i].contains(polyline.first_point()) && lower_layer.slices_bboxes[i].contains(polyline.last_point()) &&  | ||||
| 							lower_layer.slices[i].contains(polyline.first_point()) && lower_layer.slices[i].contains(polyline.last_point())) { | ||||
| 							// Offset a polyline into a thick line.
 | ||||
| 							polygons_append(bridges, offset(polyline, 0.5f * w + 10.f)); | ||||
| 							break; | ||||
| 						} | ||||
|                 } | ||||
|             bridges = union_(bridges); | ||||
|         } | ||||
|  | @ -923,7 +927,7 @@ namespace SupportMaterialInternal { | |||
|         //FIXME add supports at regular intervals to support long bridges!
 | ||||
|         bridges = diff(bridges, | ||||
|                 // Offset unsupported edges into polygons.
 | ||||
|                 offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
|                 offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
|         // Remove bridged areas from the supported areas.
 | ||||
|         contact_polygons = diff(contact_polygons, bridges, true); | ||||
|     } | ||||
|  | @ -994,7 +998,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|             // inflate the polygons over and over.
 | ||||
|             Polygons &covered = buildplate_covered[layer_id]; | ||||
|             covered = buildplate_covered[layer_id - 1]; | ||||
|             polygons_append(covered, offset(lower_layer.slices.expolygons, scale_(0.01))); | ||||
|             polygons_append(covered, offset(lower_layer.slices, scale_(0.01))); | ||||
|             covered = union_(covered, false); // don't apply the safety offset.
 | ||||
|         } | ||||
|     } | ||||
|  | @ -1023,7 +1027,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                 Polygons contact_polygons; | ||||
|                 Polygons slices_margin_cached; | ||||
|                 float    slices_margin_cached_offset = -1.; | ||||
|                 Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices.expolygons); | ||||
|                 Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices); | ||||
|                 // Offset of the lower layer, to trim the support polygons with to calculate dense supports.
 | ||||
|                 float    no_interface_offset = 0.f; | ||||
|                 if (layer_id == 0) { | ||||
|  | @ -1162,7 +1166,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                                 slices_margin_cached_offset = slices_margin_offset; | ||||
|                                 slices_margin_cached = (slices_margin_offset == 0.f) ?  | ||||
|                                     lower_layer_polygons : | ||||
|                                     offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS); | ||||
|                                     offset2(to_polygons(lower_layer.slices), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS); | ||||
|                                 if (! buildplate_covered.empty()) { | ||||
|                                     // Trim the inflated contact surfaces by the top surfaces as well.
 | ||||
|                                     polygons_append(slices_margin_cached, buildplate_covered[layer_id]); | ||||
|  | @ -1468,7 +1472,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta | |||
|                         svg.draw(union_ex(top, false), "blue", 0.5f); | ||||
|                         svg.draw(union_ex(projection_raw, true), "red", 0.5f); | ||||
|                         svg.draw_outline(union_ex(projection_raw, true), "red", "blue", scale_(0.1f)); | ||||
|                         svg.draw(layer.slices.expolygons, "green", 0.5f); | ||||
|                         svg.draw(layer.slices, "green", 0.5f); | ||||
|                     } | ||||
|         #endif /* SLIC3R_DEBUG */ | ||||
| 
 | ||||
|  | @ -1568,8 +1572,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta | |||
|             Polygons &layer_support_area = layer_support_areas[layer_id]; | ||||
|             task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area, layer_id] { | ||||
|                 // Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
 | ||||
|     //            Polygons trimming = union_(to_polygons(layer.slices.expolygons), touching, true);
 | ||||
|                 Polygons trimming = offset(layer.slices.expolygons, float(SCALED_EPSILON)); | ||||
|     //            Polygons trimming = union_(to_polygons(layer.slices), touching, true);
 | ||||
|                 Polygons trimming = offset(layer.slices, float(SCALED_EPSILON)); | ||||
|                 projection = diff(projection_raw, trimming, false); | ||||
|     #ifdef SLIC3R_DEBUG | ||||
|                 { | ||||
|  | @ -2101,7 +2105,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( | |||
|                     const Layer &object_layer = *object.layers()[i]; | ||||
|                     if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) | ||||
|                         break; | ||||
|                     polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
|                     polygons_append(polygons_trimming, offset(object_layer.slices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
|                 } | ||||
|                 if (! m_slicing_params.soluble_interface) { | ||||
|                     // Collect all bottom surfaces, which will be extruded with a bridging flow.
 | ||||
|  | @ -2214,7 +2218,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf | |||
|         // Expand the bases of the support columns in the 1st layer.
 | ||||
|         columns_base->polygons = diff( | ||||
|             offset(columns_base->polygons, inflate_factor_1st_layer), | ||||
|             offset(m_object->layers().front()->slices.expolygons, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
|             offset(m_object->layers().front()->slices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
|         if (contacts != nullptr) | ||||
|             columns_base->polygons = diff(columns_base->polygons, interface_polygons); | ||||
|     } | ||||
|  |  | |||
|  | @ -1717,13 +1717,18 @@ static void thick_point_to_verts(const Vec3crd& point, | |||
|     point_to_indexed_vertex_array(point, width, height, volume.indexed_vertex_array); | ||||
| } | ||||
| 
 | ||||
| void _3DScene::extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume) | ||||
| { | ||||
| 	if (polyline.size() >= 2) { | ||||
| 		size_t num_segments = polyline.size() - 1; | ||||
| 		thick_lines_to_verts(polyline.lines(), std::vector<double>(num_segments, width), std::vector<double>(num_segments, height), false, print_z, volume); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Fill in the qverts and tverts with quads and triangles for the extrusion_path.
 | ||||
| void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) | ||||
| { | ||||
|     Lines               lines = extrusion_path.polyline.lines(); | ||||
|     std::vector<double> widths(lines.size(), extrusion_path.width); | ||||
|     std::vector<double> heights(lines.size(), extrusion_path.height); | ||||
|     thick_lines_to_verts(lines, widths, heights, false, print_z, volume); | ||||
| 	extrusionentity_to_verts(extrusion_path.polyline, extrusion_path.width, extrusion_path.height, print_z, volume); | ||||
| } | ||||
| 
 | ||||
| // Fill in the qverts and tverts with quads and triangles for the extrusion_path.
 | ||||
|  |  | |||
|  | @ -656,6 +656,7 @@ public: | |||
| 
 | ||||
|     static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume); | ||||
|     static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume); | ||||
| 	static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume); | ||||
|     static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); | ||||
|     static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); | ||||
|     static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); | ||||
|  |  | |||
|  | @ -354,8 +354,7 @@ void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) | |||
|     toggle_field("pad_wall_slope", pad_en); | ||||
|     toggle_field("pad_around_object", pad_en); | ||||
| 
 | ||||
|     bool has_suppad = pad_en && supports_en; | ||||
|     bool zero_elev = config->opt_bool("pad_around_object") && has_suppad; | ||||
|     bool zero_elev = config->opt_bool("pad_around_object") && pad_en; | ||||
| 
 | ||||
|     toggle_field("support_object_elevation", supports_en && !zero_elev); | ||||
|     toggle_field("pad_object_gap", zero_elev); | ||||
|  |  | |||
|  | @ -4987,19 +4987,21 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat | |||
|     // helper functions to select data in dependence of the extrusion view type
 | ||||
|     struct Helper | ||||
|     { | ||||
|         static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path) | ||||
|         static float path_filter(GCodePreviewData::Extrusion::EViewType type, const GCodePreviewData::Extrusion::Path& path) | ||||
|         { | ||||
|             switch (type) | ||||
|             { | ||||
|             case GCodePreviewData::Extrusion::FeatureType: | ||||
|             	// The role here is used for coloring.
 | ||||
|                 return (float)path.role(); | ||||
|                 return (float)path.extrusion_role; | ||||
|             case GCodePreviewData::Extrusion::Height: | ||||
|                 return path.height; | ||||
|             case GCodePreviewData::Extrusion::Width: | ||||
|                 return path.width; | ||||
|             case GCodePreviewData::Extrusion::Feedrate: | ||||
|                 return path.feedrate; | ||||
|             case GCodePreviewData::Extrusion::FanSpeed: | ||||
|                 return path.fan_speed; | ||||
|             case GCodePreviewData::Extrusion::VolumetricRate: | ||||
|                 return path.feedrate * (float)path.mm3_per_mm; | ||||
|             case GCodePreviewData::Extrusion::Tool: | ||||
|  | @ -5025,6 +5027,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat | |||
|                 return data.get_width_color(value); | ||||
|             case GCodePreviewData::Extrusion::Feedrate: | ||||
|                 return data.get_feedrate_color(value); | ||||
|             case GCodePreviewData::Extrusion::FanSpeed: | ||||
|                 return data.get_fan_speed_color(value); | ||||
|             case GCodePreviewData::Extrusion::VolumetricRate: | ||||
|                 return data.get_volumetric_rate_color(value); | ||||
|             case GCodePreviewData::Extrusion::Tool: | ||||
|  | @ -5067,15 +5071,15 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat | |||
| 	    { | ||||
| 		    std::vector<size_t> num_paths_per_role(size_t(erCount), 0); | ||||
| 		    for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers) | ||||
| 		        for (const ExtrusionPath &path : layer.paths) | ||||
| 		        	++ num_paths_per_role[size_t(path.role())]; | ||||
| 		        for (const GCodePreviewData::Extrusion::Path &path : layer.paths) | ||||
| 		        	++ num_paths_per_role[size_t(path.extrusion_role)]; | ||||
|             std::vector<std::vector<float>> roles_values; | ||||
| 			roles_values.assign(size_t(erCount), std::vector<float>()); | ||||
| 		    for (size_t i = 0; i < roles_values.size(); ++ i) | ||||
| 		    	roles_values[i].reserve(num_paths_per_role[i]); | ||||
|             for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) | ||||
| 		        for (const ExtrusionPath& path : layer.paths) | ||||
| 		        	roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path)); | ||||
| 		        for (const GCodePreviewData::Extrusion::Path &path : layer.paths) | ||||
| 		        	roles_values[size_t(path.extrusion_role)].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path)); | ||||
|             roles_filters.reserve(size_t(erCount)); | ||||
| 			size_t num_buffers = 0; | ||||
| 		    for (std::vector<float> &values : roles_values) { | ||||
|  | @ -5103,9 +5107,9 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat | |||
| 	    // populates volumes
 | ||||
| 		for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) | ||||
| 		{ | ||||
| 			for (const ExtrusionPath& path : layer.paths) | ||||
| 			for (const GCodePreviewData::Extrusion::Path& path : layer.paths) | ||||
| 			{ | ||||
| 				std::vector<std::pair<float, GLVolume*>> &filters = roles_filters[size_t(path.role())]; | ||||
| 				std::vector<std::pair<float, GLVolume*>> &filters = roles_filters[size_t(path.extrusion_role)]; | ||||
| 				auto key = std::make_pair<float, GLVolume*>(Helper::path_filter(preview_data.extrusion.view_type, path), nullptr); | ||||
| 				auto it_filter = std::lower_bound(filters.begin(), filters.end(), key); | ||||
| 				assert(it_filter != filters.end() && key.first == it_filter->first); | ||||
|  | @ -5115,7 +5119,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat | |||
| 				vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); | ||||
| 				vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); | ||||
| 
 | ||||
| 				_3DScene::extrusionentity_to_verts(path, layer.z, vol); | ||||
| 				_3DScene::extrusionentity_to_verts(path.polyline, path.width, path.height, layer.z, vol); | ||||
| 			} | ||||
| 			// Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
 | ||||
| 		    for (std::vector<std::pair<float, GLVolume*>> &filters : roles_filters) { | ||||
|  |  | |||
|  | @ -131,7 +131,7 @@ ObjectList::ObjectList(wxWindow* parent) : | |||
|         { | ||||
|             wxDataViewItemArray sels; | ||||
|             GetSelections(sels); | ||||
|             if (sels.front() == m_last_selected_item) | ||||
|             if (! sels.empty() && sels.front() == m_last_selected_item) | ||||
|                 m_last_selected_item = sels.back(); | ||||
|             else | ||||
|                 m_last_selected_item = event.GetItem(); | ||||
|  |  | |||
|  | @ -221,6 +221,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view | |||
|     m_choice_view_type->Append(_(L("Height"))); | ||||
|     m_choice_view_type->Append(_(L("Width"))); | ||||
|     m_choice_view_type->Append(_(L("Speed"))); | ||||
|     m_choice_view_type->Append(_(L("Fan speed"))); | ||||
|     m_choice_view_type->Append(_(L("Volumetric flow rate"))); | ||||
|     m_choice_view_type->Append(_(L("Tool"))); | ||||
|     m_choice_view_type->Append(_(L("Color Print"))); | ||||
|  |  | |||
|  | @ -1808,7 +1808,10 @@ void TabPrinter::build_fff() | |||
|         optgroup->append_single_option_line("single_extruder_multi_material"); | ||||
| 
 | ||||
|         optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { | ||||
|             size_t extruders_count = boost::any_cast<size_t>(optgroup->get_value("extruders_count")); | ||||
|             // optgroup->get_value() return int for def.type == coInt,
 | ||||
|             // Thus, there should be boost::any_cast<int> !
 | ||||
|             // Otherwise, boost::any_cast<size_t> causes an "unhandled unknown exception"
 | ||||
|             size_t extruders_count = size_t(boost::any_cast<int>(optgroup->get_value("extruders_count"))); | ||||
|             wxTheApp->CallAfter([this, opt_key, value, extruders_count]() { | ||||
|                 if (opt_key == "extruders_count" || opt_key == "single_extruder_multi_material") { | ||||
|                     extruders_count_changed(extruders_count); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 YuSanka
						YuSanka