#ifndef slic3r_Layer_hpp_ #define slic3r_Layer_hpp_ #include "libslic3r.h" #include "BoundingBox.hpp" #include "Flow.hpp" #include "SurfaceCollection.hpp" #include "ExtrusionEntityCollection.hpp" #include namespace Slic3r { class ExPolygon; using ExPolygons = std::vector; class Layer; using LayerPtrs = std::vector; class LayerRegion; using LayerRegionPtrs = std::vector; class PrintRegion; class PrintObject; namespace FillAdaptive { struct Octree; } namespace FillLightning { class Generator; }; // Range of indices, providing support for range based loops. template class IndexRange { public: IndexRange(T ibegin, T iend) : m_begin(ibegin), m_end(iend) {} IndexRange() = default; // Just a bare minimum functionality iterator required by range-for loop. class Iterator { public: T operator*() const { return m_idx; } bool operator!=(const Iterator &rhs) const { return m_idx != rhs.m_idx; } void operator++() { ++ m_idx; } private: friend class IndexRange; Iterator(T idx) : m_idx(idx) {} T m_idx; }; Iterator begin() const { assert(m_begin <= m_end); return Iterator(m_begin); }; Iterator end() const { assert(m_begin <= m_end); return Iterator(m_end); }; bool empty() const { assert(m_begin <= m_end); return m_begin >= m_end; } T size() const { assert(m_begin <= m_end); return m_end - m_begin; } private: // Index of the first extrusion in LayerRegion. T m_begin { 0 }; // Index of the last extrusion in LayerRegion. T m_end { 0 }; }; using ExtrusionRange = IndexRange; using ExPolygonRange = IndexRange; // Range of extrusions, referencing the source region by an index. class LayerExtrusionRange : public ExtrusionRange { public: LayerExtrusionRange(uint32_t iregion, ExtrusionRange extrusion_range) : m_region(iregion), ExtrusionRange(extrusion_range) {} LayerExtrusionRange() = default; // Index of LayerRegion in Layer. uint32_t region() const { return m_region; }; private: // Index of LayerRegion in Layer. uint32_t m_region { 0 }; }; // One LayerIsland may be filled with solid fill, sparse fill, top / bottom fill. static constexpr const size_t LayerExtrusionRangesStaticSize = 3; using LayerExtrusionRanges = #ifdef NDEBUG // To reduce memory allocation in release mode. boost::container::small_vector; #else // NDEBUG // To ease debugging. std::vector; #endif // NDEBUG class LayerRegion { public: [[nodiscard]] Layer* layer() { return m_layer; } [[nodiscard]] const Layer* layer() const { return m_layer; } [[nodiscard]] const PrintRegion& region() const { return *m_region; } // collection of surfaces generated by slicing the original geometry // divided by type top/bottom/internal [[nodiscard]] const SurfaceCollection& slices() const { return m_slices; } // Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature") // and for re-starting of infills. [[nodiscard]] const ExPolygons& fill_expolygons() const { return m_fill_expolygons; } // and their bounding boxes [[nodiscard]] const BoundingBoxes& fill_expolygons_bboxes() const { return m_fill_expolygons_bboxes; } // Storage for fill regions produced for a single LayerIsland, of which infill splits into multiple islands. // Not used for a plain single material print with no infill modifiers. [[nodiscard]] const ExPolygons& fill_expolygons_composite() const { return m_fill_expolygons_composite; } // and their bounding boxes [[nodiscard]] const BoundingBoxes& fill_expolygons_composite_bboxes() const { return m_fill_expolygons_composite_bboxes; } // collection of surfaces generated by slicing the original geometry // divided by type top/bottom/internal [[nodiscard]] const SurfaceCollection& fill_surfaces() const { return m_fill_surfaces; } // collection of extrusion paths/loops filling gaps // These fills are generated by the perimeter generator. // They are not printed on their own, but they are copied to this->fills during infill generation. [[nodiscard]] const ExtrusionEntityCollection& thin_fills() const { return m_thin_fills; } // collection of polylines representing the unsupported bridge edges [[nodiscard]] const Polylines& unsupported_bridge_edges() const { return m_unsupported_bridge_edges; } // ordered collection of extrusion paths/loops to build all perimeters // (this collection contains only ExtrusionEntityCollection objects) [[nodiscard]] const ExtrusionEntityCollection& perimeters() const { return m_perimeters; } // ordered collection of extrusion paths to fill surfaces // (this collection contains only ExtrusionEntityCollection objects) [[nodiscard]] const ExtrusionEntityCollection& fills() const { return m_fills; } Flow flow(FlowRole role) const; Flow flow(FlowRole role, double layer_height) const; Flow bridging_flow(FlowRole role, bool force_thick_bridges = false) const; void slices_to_fill_surfaces_clipped(); void prepare_fill_surfaces(); // Produce perimeter extrusions, gap fill extrusions and fill polygons for input slices. void make_perimeters( // Input slices for which the perimeters, gap fills and fill expolygons are to be generated. const SurfaceCollection &slices, // Ranges of perimeter extrusions and gap fill extrusions per suface, referencing // newly created extrusions stored at this LayerRegion. std::vector> &perimeter_and_gapfill_ranges, // All fill areas produced for all input slices above. ExPolygons &fill_expolygons, // Ranges of fill areas above per input slice. std::vector &fill_expolygons_ranges); void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered); double infill_area_threshold() const; // Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer. void trim_surfaces(const Polygons &trimming_polygons); // Single elephant foot compensation step, used by the elephant foor compensation at the 1st layer. // Trim surfaces by trimming polygons (shrunk by an elephant foot compensation step), but don't shrink narrow parts so much that no perimeter would fit. void elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons); void export_region_slices_to_svg(const char *path) const; void export_region_fill_surfaces_to_svg(const char *path) const; // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export. void export_region_slices_to_svg_debug(const char *name) const; void export_region_fill_surfaces_to_svg_debug(const char *name) const; // Is there any valid extrusion assigned to this LayerRegion? bool has_extrusions() const { return ! this->perimeters().empty() || ! this->fills().empty(); } protected: friend class Layer; friend class PrintObject; LayerRegion(Layer *layer, const PrintRegion *region) : m_layer(layer), m_region(region) {} ~LayerRegion() = default; private: // Modifying m_slices friend std::string fix_slicing_errors(LayerPtrs&, const std::function&); template friend void apply_mm_segmentation(PrintObject& print_object, ThrowOnCancel throw_on_cancel); Layer *m_layer; const PrintRegion *m_region; // Backed up slices before they are split into top/bottom/internal. // Only backed up for multi-region layers or layers with elephant foot compensation. //FIXME Review whether not to simplify the code by keeping the raw_slices all the time. ExPolygons m_raw_slices; //FIXME make m_slices public for unit tests public: // collection of surfaces generated by slicing the original geometry // divided by type top/bottom/internal SurfaceCollection m_slices; private: // Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature") // and for re-starting of infills. ExPolygons m_fill_expolygons; // and their bounding boxes BoundingBoxes m_fill_expolygons_bboxes; // Storage for fill regions produced for a single LayerIsland, of which infill splits into multiple islands. // Not used for a plain single material print with no infill modifiers. ExPolygons m_fill_expolygons_composite; // and their bounding boxes BoundingBoxes m_fill_expolygons_composite_bboxes; // Collection of surfaces for infill generation, created by splitting m_slices by m_fill_expolygons. SurfaceCollection m_fill_surfaces; // Collection of extrusion paths/loops filling gaps // These fills are generated by the perimeter generator. // They are not printed on their own, but they are copied to this->fills during infill generation. ExtrusionEntityCollection m_thin_fills; // collection of polylines representing the unsupported bridge edges Polylines m_unsupported_bridge_edges; // ordered collection of extrusion paths/loops to build all perimeters // (this collection contains only ExtrusionEntityCollection objects) ExtrusionEntityCollection m_perimeters; // ordered collection of extrusion paths to fill surfaces // (this collection contains only ExtrusionEntityCollection objects) ExtrusionEntityCollection m_fills; // collection of expolygons representing the bridged areas (thus not // needing support material) // Polygons bridged; }; // LayerSlice contains one or more LayerIsland objects, // each LayerIsland containing a set of perimeter extrusions extruded with one particular PrintRegionConfig parameters // and one or multiple struct LayerIsland { private: friend class Layer; static constexpr const uint32_t fill_region_composite_id = std::numeric_limits::max(); public: // Perimeter extrusions in LayerRegion belonging to this island. LayerExtrusionRange perimeters; // Thin fills of the same region as perimeters. Generated by classic perimeter generator, while Arachne puts them into perimeters. ExtrusionRange thin_fills; // Infill + gapfill extrusions in LayerRegion belonging to this island. LayerExtrusionRanges fills; // Region that is to be filled with the fills above (thin fills, regular fills). // Pointing to either LayerRegion::fill_expolygons() or LayerRegion::fill_expolygons_composite() // based on this->fill_expolygons_composite() flag. ExPolygonRange fill_expolygons; // Index of LayerRegion with LayerRegion::fill_expolygons() if not fill_expolygons_composite(). uint32_t fill_region_id; bool fill_expolygons_composite() const { return this->fill_region_id == fill_region_composite_id; } // Centroid of this island used for path planning. // Point centroid; bool has_extrusions() const { return ! this->perimeters.empty() || ! this->fills.empty(); } void add_fill_range(const LayerExtrusionRange &new_fill_range) { // Compress ranges. if (! this->fills.empty() && this->fills.back().region() == new_fill_range.region() && *this->fills.back().end() == *new_fill_range.begin()) this->fills.back() = { new_fill_range.region(), { *this->fills.back().begin(), *new_fill_range.end() } }; else this->fills.push_back(new_fill_range); } }; static constexpr const size_t LayerIslandsStaticSize = 1; using LayerIslands = #ifdef NDEBUG // To reduce memory allocation in release mode. boost::container::small_vector; #else // NDEBUG // To ease debugging. std::vector; #endif // NDEBUG // One connected island of a layer. LayerSlice may consist of one or more LayerIslands. struct LayerSlice { struct Link { int32_t slice_idx; float area; }; static constexpr const size_t LinksStaticSize = 4; using Links = #ifdef NDEBUG // To reduce memory allocation in release mode. boost::container::small_vector; #else // NDEBUG // To ease debugging. std::vector; #endif // NDEBUG BoundingBox bbox; Links overlaps_above; Links overlaps_below; // One island for each region or region set that generates its own perimeters. // For multi-material prints or prints with regions of different perimeter parameters, // a LayerSlice may be split into multiple LayerIslands. // For most prints there will be just one island. LayerIslands islands; bool has_extrusions() const { for (const LayerIsland &island : islands) if (island.has_extrusions()) return true; return false; } }; using LayerSlices = std::vector; class Layer { public: // Sequential index of this layer in PrintObject::m_layers, offsetted by the number of raft layers. size_t id() const { return m_id; } void set_id(size_t id) { m_id = id; } PrintObject* object() { return m_object; } const PrintObject* object() const { return m_object; } Layer *upper_layer; Layer *lower_layer; bool slicing_errors; coordf_t slice_z; // Z used for slicing in unscaled coordinates coordf_t print_z; // Z used for printing in unscaled coordinates coordf_t height; // layer height in unscaled coordinates coordf_t bottom_z() const { return this->print_z - this->height; } //Lines estimated to be seriously malformed, info from the IssueSearch algorithm. These lines should probably be avoided during fast travels. Lines malformed_lines; // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // (with possibly differing extruder ID and slicing parameters) and merged. // For the first layer, if the Elephant foot compensation is applied, this lslice is uncompensated, therefore // it includes the Elephant foot effect, thus it corresponds to the shape of the printed 1st layer. // These lslices aka islands are chained by the shortest traverse distance and this traversal // order will be applied by the G-code generator to the extrusions fitting into these lslices. // These lslices are also used to detect overhangs and overlaps between successive layers, therefore it is important // that the 1st lslice is not compensated by the Elephant foot compensation algorithm. ExPolygons lslices; LayerSlices lslices_ex; size_t region_count() const { return m_regions.size(); } const LayerRegion* get_region(int idx) const { return m_regions[idx]; } LayerRegion* get_region(int idx) { return m_regions[idx]; } LayerRegion* add_region(const PrintRegion *print_region); const LayerRegionPtrs& regions() const { return m_regions; } // Test whether whether there are any slices assigned to this layer. bool empty() const; void make_slices(); // After creating the slices on all layers, chain the islands overlapping in Z. static void build_up_down_graph(Layer &below, Layer &above); // Backup and restore raw sliced regions if needed. //FIXME Review whether not to simplify the code by keeping the raw_slices all the time. void backup_untyped_slices(); void restore_untyped_slices(); // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. void restore_untyped_slices_no_extra_perimeters(); // Slices merged into islands, to be used by the elephant foot compensation to trim the individual surfaces with the shrunk merged slices. ExPolygons merged(float offset) const; template bool any_internal_region_slice_contains(const T &item) const { for (const LayerRegion *layerm : m_regions) if (layerm->slices().any_internal_contains(item)) return true; return false; } template bool any_bottom_region_slice_contains(const T &item) const { for (const LayerRegion *layerm : m_regions) if (layerm->slices().any_bottom_contains(item)) return true; return false; } void make_perimeters(); // Phony version of make_fills() without parameters for Perl integration only. void make_fills() { this->make_fills(nullptr, nullptr, nullptr); } void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator); void make_ironing(); void export_region_slices_to_svg(const char *path) const; void export_region_fill_surfaces_to_svg(const char *path) const; // Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export. void export_region_slices_to_svg_debug(const char *name) const; void export_region_fill_surfaces_to_svg_debug(const char *name) const; // Is there any valid extrusion assigned to this LayerRegion? virtual bool has_extrusions() const { for (auto layerm : m_regions) if (layerm->has_extrusions()) return true; return false; } // virtual bool has_extrusions() const { for (const LayerSlice &lslice : lslices_ex) if (lslice.has_extrusions()) return true; return false; } protected: friend class PrintObject; friend std::vector new_layers(PrintObject*, const std::vector&); friend std::string fix_slicing_errors(LayerPtrs&, const std::function&); Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false), slice_z(slice_z), print_z(print_z), height(height), m_id(id), m_object(object) {} virtual ~Layer(); // Clear fill extrusions, remove them from layer islands. void clear_fills(); private: void sort_perimeters_into_islands( // Slices for which perimeters and fill_expolygons were just created. // The slices may have been created by merging multiple source slices with the same perimeter parameters. const SurfaceCollection &slices, // Region where the perimeters, gap fills and fill expolygons are stored. const uint32_t region_id, // Perimeters and gap fills produced by the perimeter generator for the slices, // sorted by the source slices. const std::vector> &perimeter_and_gapfill_ranges, // Fill expolygons produced for all source slices above. ExPolygons &&fill_expolygons, // Fill expolygon ranges sorted by the source slices. const std::vector &fill_expolygons_ranges, // If the current layer consists of multiple regions, then the fill_expolygons above are split by the source LayerRegion surfaces. const std::vector &layer_region_ids); // Sequential index of layer, 0-based, offsetted by number of raft layers. size_t m_id; PrintObject *m_object; LayerRegionPtrs m_regions; }; class SupportLayer : public Layer { public: // Polygons covered by the supports: base, interface and contact areas. // Used to suppress retraction if moving for a support extrusion over these support_islands. ExPolygons support_islands; // Slightly inflated bounding boxes of the above, for faster intersection query. BoundingBoxes support_islands_bboxes; // Extrusion paths for the support base and for the support interface and contacts. ExtrusionEntityCollection support_fills; // Is there any valid extrusion assigned to this LayerRegion? virtual bool has_extrusions() const { return ! support_fills.empty(); } // Zero based index of an interface layer, used for alternating direction of interface / contact layers. size_t interface_id() const { return m_interface_id; } protected: friend class PrintObject; // The constructor has been made public to be able to insert additional support layers for the skirt or a wipe tower // between the raft and the object first layer. SupportLayer(size_t id, size_t interface_id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : Layer(id, object, height, print_z, slice_z), m_interface_id(interface_id) {} virtual ~SupportLayer() = default; size_t m_interface_id; }; template inline std::vector zs_from_layers(const LayerContainer &layers) { std::vector zs; zs.reserve(layers.size()); for (const Layer *l : layers) zs.emplace_back((float)l->slice_z); return zs; } extern BoundingBox get_extents(const LayerRegion &layer_region); extern BoundingBox get_extents(const LayerRegionPtrs &layer_regions); } #endif