mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 10:59:03 +08:00
Refactor: Move "LayerRegion" to separate header and add a test.
Move declarations associated with LayerRegion to a separate header file. Add test for bridge surface expansion to enable further refactoring.
This commit is contained in:
parent
5dc1699b0e
commit
9797110a96
@ -221,8 +221,10 @@ set(SLIC3R_SOURCES
|
|||||||
JumpPointSearch.cpp
|
JumpPointSearch.cpp
|
||||||
JumpPointSearch.hpp
|
JumpPointSearch.hpp
|
||||||
KDTreeIndirect.hpp
|
KDTreeIndirect.hpp
|
||||||
|
IndexRange.hpp
|
||||||
Layer.cpp
|
Layer.cpp
|
||||||
Layer.hpp
|
Layer.hpp
|
||||||
|
LayerRegion.hpp
|
||||||
LayerRegion.cpp
|
LayerRegion.cpp
|
||||||
libslic3r.h
|
libslic3r.h
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h"
|
"${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h"
|
||||||
|
44
src/libslic3r/IndexRange.hpp
Normal file
44
src/libslic3r/IndexRange.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef slic3r_IndexRange_hpp_
|
||||||
|
#define slic3r_IndexRange_hpp_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
// Range of indices, providing support for range based loops.
|
||||||
|
template<typename T>
|
||||||
|
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<T>;
|
||||||
|
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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
template class IndexRange<uint32_t>;
|
||||||
|
} // Slic3r
|
||||||
|
|
||||||
|
#endif // slic3r_IndexRange_hpp_
|
@ -17,6 +17,8 @@
|
|||||||
#include "Flow.hpp"
|
#include "Flow.hpp"
|
||||||
#include "SurfaceCollection.hpp"
|
#include "SurfaceCollection.hpp"
|
||||||
#include "ExtrusionEntityCollection.hpp"
|
#include "ExtrusionEntityCollection.hpp"
|
||||||
|
#include "IndexRange.hpp"
|
||||||
|
#include "LayerRegion.hpp"
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
@ -39,42 +41,6 @@ namespace FillLightning {
|
|||||||
class Generator;
|
class Generator;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Range of indices, providing support for range based loops.
|
|
||||||
template<typename T>
|
|
||||||
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<T>;
|
|
||||||
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<uint32_t>;
|
|
||||||
using ExPolygonRange = IndexRange<uint32_t>;
|
|
||||||
|
|
||||||
// Range of extrusions, referencing the source region by an index.
|
// Range of extrusions, referencing the source region by an index.
|
||||||
class LayerExtrusionRange : public ExtrusionRange
|
class LayerExtrusionRange : public ExtrusionRange
|
||||||
{
|
{
|
||||||
@ -101,144 +67,6 @@ using LayerExtrusionRanges =
|
|||||||
std::vector<LayerExtrusionRange>;
|
std::vector<LayerExtrusionRange>;
|
||||||
#endif // NDEBUG
|
#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<std::pair<ExtrusionRange, ExtrusionRange>> &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<ExPolygonRange> &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<void()>&);
|
|
||||||
template<typename ThrowOnCancel>
|
|
||||||
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,
|
// LayerSlice contains one or more LayerIsland objects,
|
||||||
// each LayerIsland containing a set of perimeter extrusions extruded with one particular PrintRegionConfig parameters
|
// each LayerIsland containing a set of perimeter extrusions extruded with one particular PrintRegionConfig parameters
|
||||||
|
175
src/libslic3r/LayerRegion.hpp
Normal file
175
src/libslic3r/LayerRegion.hpp
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
#ifndef slic3r_LayerRegion_hpp_
|
||||||
|
#define slic3r_LayerRegion_hpp_
|
||||||
|
|
||||||
|
#include "BoundingBox.hpp"
|
||||||
|
#include "ExtrusionEntityCollection.hpp"
|
||||||
|
#include "SurfaceCollection.hpp"
|
||||||
|
#include "IndexRange.hpp"
|
||||||
|
#include "libslic3r/Algorithm/RegionExpansion.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class Layer;
|
||||||
|
using LayerPtrs = std::vector<Layer*>;
|
||||||
|
class PrintRegion;
|
||||||
|
|
||||||
|
using ExtrusionRange = IndexRange<uint32_t>;
|
||||||
|
using ExPolygonRange = IndexRange<uint32_t>;
|
||||||
|
|
||||||
|
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<std::pair<ExtrusionRange, ExtrusionRange>> &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<ExPolygonRange> &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<void()>&);
|
||||||
|
template<typename ThrowOnCancel>
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params,
|
||||||
|
* detect bridges.
|
||||||
|
* Trim "shells" by the expanded bridges.
|
||||||
|
*/
|
||||||
|
Surfaces expand_bridges_detect_orientations(
|
||||||
|
Surfaces &surfaces,
|
||||||
|
ExPolygons &shells,
|
||||||
|
const Algorithm::RegionExpansionParameters &expansion_params_into_solid_infill,
|
||||||
|
ExPolygons &sparse,
|
||||||
|
const Algorithm::RegionExpansionParameters &expansion_params_into_sparse_infill,
|
||||||
|
const float closing_radius
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // slic3r_LayerRegion_hpp_
|
@ -42,6 +42,7 @@ add_executable(${_TEST_NAME}_tests
|
|||||||
test_anyptr.cpp
|
test_anyptr.cpp
|
||||||
test_jump_point_search.cpp
|
test_jump_point_search.cpp
|
||||||
test_support_spots_generator.cpp
|
test_support_spots_generator.cpp
|
||||||
|
test_layer_region.cpp
|
||||||
../data/prusaparts.cpp
|
../data/prusaparts.cpp
|
||||||
../data/prusaparts.hpp
|
../data/prusaparts.hpp
|
||||||
test_static_map.cpp
|
test_static_map.cpp
|
||||||
|
98
tests/libslic3r/test_layer_region.cpp
Normal file
98
tests/libslic3r/test_layer_region.cpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#include "libslic3r/ClipperUtils.hpp"
|
||||||
|
#include "libslic3r/Point.hpp"
|
||||||
|
#include "libslic3r/SVG.hpp"
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include <libslic3r/LayerRegion.hpp>
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
constexpr bool export_svgs = false;
|
||||||
|
|
||||||
|
ExPolygon rectangle(const Point& origin, const int width, const int height) {
|
||||||
|
return {
|
||||||
|
origin,
|
||||||
|
origin + Point{width, 0},
|
||||||
|
origin + Point{width, height},
|
||||||
|
origin + Point{0, height},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("test the bridge expansion with the bridge angle detection", "[LayerRegion]") {
|
||||||
|
using namespace Slic3r::Algorithm;
|
||||||
|
Surfaces surfaces{
|
||||||
|
Surface{
|
||||||
|
stBottomBridge,
|
||||||
|
rectangle({scaled(-1.0), scaled(0.0)}, scaled(1.0), scaled(1.0))
|
||||||
|
},
|
||||||
|
Surface{
|
||||||
|
stBottomBridge,
|
||||||
|
rectangle({scaled(0.0), scaled(0.0)}, scaled(1.0), scaled(1.0))
|
||||||
|
},
|
||||||
|
Surface{
|
||||||
|
stBottomBridge,
|
||||||
|
rectangle({scaled(-3.0), scaled(0.0)}, scaled(1.0), scaled(1.0))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ExPolygons shells{{
|
||||||
|
rectangle({scaled(-1.0), scaled(1.0)}, scaled(3.0), scaled(1.0))
|
||||||
|
}};
|
||||||
|
ExPolygons sparse {{
|
||||||
|
rectangle({scaled(-2.0), scaled(-1.0)}, scaled(1.0), scaled(3.0))
|
||||||
|
}};
|
||||||
|
|
||||||
|
const float scaled_spacing{scaled(0.3)};
|
||||||
|
|
||||||
|
static constexpr const float expansion_step = scaled<float>(0.1);
|
||||||
|
// Don't take more than max_nr_steps for small expansion_step.
|
||||||
|
static constexpr const size_t max_nr_expansion_steps = 5;
|
||||||
|
const float closing_radius = 0.55f * 0.65f * 1.05f * scaled_spacing;
|
||||||
|
const int shells_expansion_depth = scaled(0.6);
|
||||||
|
const auto expansion_params_into_solid_infill = RegionExpansionParameters::build(
|
||||||
|
shells_expansion_depth,
|
||||||
|
expansion_step,
|
||||||
|
max_nr_expansion_steps
|
||||||
|
);
|
||||||
|
const int sparse_expansion_depth = scaled(0.3);
|
||||||
|
const auto expansion_params_into_sparse_infill = RegionExpansionParameters::build(
|
||||||
|
sparse_expansion_depth,
|
||||||
|
expansion_step,
|
||||||
|
max_nr_expansion_steps
|
||||||
|
);
|
||||||
|
|
||||||
|
Surfaces result{expand_bridges_detect_orientations(
|
||||||
|
surfaces,
|
||||||
|
shells,
|
||||||
|
expansion_params_into_solid_infill,
|
||||||
|
sparse,
|
||||||
|
expansion_params_into_sparse_infill, closing_radius
|
||||||
|
)};
|
||||||
|
|
||||||
|
if constexpr (export_svgs) {
|
||||||
|
SVG svg("bridge_expansion.svg", BoundingBox{
|
||||||
|
Point{scaled(-3.0), scaled(-1.0)},
|
||||||
|
Point{scaled(2.0), scaled(2.0)}
|
||||||
|
});
|
||||||
|
|
||||||
|
svg.draw(surfaces, "blue");
|
||||||
|
svg.draw(shells, "green");
|
||||||
|
svg.draw(sparse, "red");
|
||||||
|
svg.draw_outline(result, "black", "", scale_(0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(result.size() == 2);
|
||||||
|
CHECK(result.at(0).bridge_angle == Approx(1.5707963268));
|
||||||
|
CHECK(result.at(1).bridge_angle == Approx(0));
|
||||||
|
CHECK(result.at(0).expolygon.contour.size() == 22);
|
||||||
|
CHECK(result.at(1).expolygon.contour.size() == 14);
|
||||||
|
|
||||||
|
// These lines in the polygons should correspond to the expansion depth.
|
||||||
|
CHECK(result.at(0).expolygon.contour.lines().at(2).length() == shells_expansion_depth);
|
||||||
|
CHECK(result.at(1).expolygon.contour.lines().at(7).length() == sparse_expansion_depth);
|
||||||
|
CHECK(result.at(1).expolygon.contour.lines().at(11).length() == sparse_expansion_depth);
|
||||||
|
|
||||||
|
CHECK(intersection_ex({result.at(0).expolygon}, sparse).size() == 0);
|
||||||
|
CHECK(intersection_ex({result.at(0).expolygon}, shells).size() == 0);
|
||||||
|
CHECK(intersection_ex({result.at(1).expolygon}, sparse).size() == 0);
|
||||||
|
CHECK(intersection_ex({result.at(1).expolygon}, shells).size() == 0);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user