Update files related to support to match BambuStudio's project structure

This commit is contained in:
Noisyfox 2024-09-30 13:34:59 +08:00
parent 1b367b7df9
commit 97d1745e5a
28 changed files with 7619 additions and 16152 deletions

View file

@ -307,29 +307,21 @@ set(lisbslic3r_sources
SlicingAdaptive.hpp SlicingAdaptive.hpp
Support/SupportCommon.cpp Support/SupportCommon.cpp
Support/SupportCommon.hpp Support/SupportCommon.hpp
Support/SupportDebug.cpp
Support/SupportDebug.hpp
Support/SupportLayer.hpp Support/SupportLayer.hpp
# Support/SupportMaterial.cpp Support/SupportMaterial.cpp
# Support/SupportMaterial.hpp Support/SupportMaterial.hpp
Support/SupportParameters.cpp
Support/SupportParameters.hpp Support/SupportParameters.hpp
Support/OrganicSupport.cpp Support/SupportSpotsGenerator.cpp
Support/OrganicSupport.hpp Support/SupportSpotsGenerator.hpp
Support/TreeSupport.cpp
Support/TreeSupport.hpp Support/TreeSupport.hpp
Support/TreeSupportCommon.cpp Support/TreeSupport.cpp
Support/TreeSupport3D.cpp
Support/TreeSupport3D.hpp
Support/TreeSupportCommon.hpp Support/TreeSupportCommon.hpp
Support/TreeModelVolumes.cpp Support/TreeModelVolumes.cpp
Support/TreeModelVolumes.hpp Support/TreeModelVolumes.hpp
SupportMaterial.cpp
SupportMaterial.hpp
PrincipalComponents2D.cpp PrincipalComponents2D.cpp
PrincipalComponents2D.hpp PrincipalComponents2D.hpp
SupportSpotsGenerator.cpp
SupportSpotsGenerator.hpp
TreeSupport.hpp
TreeSupport.cpp
MinimumSpanningTree.hpp MinimumSpanningTree.hpp
MinimumSpanningTree.cpp MinimumSpanningTree.cpp
Surface.cpp Surface.cpp

View file

@ -9,7 +9,6 @@
#include "Geometry/ConvexHull.hpp" #include "Geometry/ConvexHull.hpp"
#include "I18N.hpp" #include "I18N.hpp"
#include "ShortestPath.hpp" #include "ShortestPath.hpp"
#include "Support/SupportMaterial.hpp"
#include "Thread.hpp" #include "Thread.hpp"
#include "Time.hpp" #include "Time.hpp"
#include "GCode.hpp" #include "GCode.hpp"

View file

@ -8,9 +8,10 @@
#include "Layer.hpp" #include "Layer.hpp"
#include "MutablePolygon.hpp" #include "MutablePolygon.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
#include "SupportMaterial.hpp" #include "Support/SupportMaterial.hpp"
#include "SupportSpotsGenerator.hpp" #include "Support/SupportSpotsGenerator.hpp"
#include "Support/TreeSupport.hpp" #include "Support/TreeSupport.hpp"
#include "Support/TreeSupport3D.hpp"
#include "Surface.hpp" #include "Surface.hpp"
#include "Slicing.hpp" #include "Slicing.hpp"
#include "Tesselate.hpp" #include "Tesselate.hpp"
@ -19,7 +20,6 @@
#include "Fill/FillAdaptive.hpp" #include "Fill/FillAdaptive.hpp"
#include "Fill/FillLightning.hpp" #include "Fill/FillLightning.hpp"
#include "Format/STL.hpp" #include "Format/STL.hpp"
#include "TreeSupport.hpp"
#include "format.hpp" #include "format.hpp"
#include <float.h> #include <float.h>
@ -3528,7 +3528,7 @@ void PrintObject::_generate_support_material()
if (this->config().support_style.value == smsOrganic || if (this->config().support_style.value == smsOrganic ||
// Orca: use organic as default // Orca: use organic as default
this->config().support_style.value == smsDefault) { this->config().support_style.value == smsDefault) {
fff_tree_support_generate(*this, std::function<void()>([this]() { this->throw_if_canceled(); })); generate_tree_support_3D(*this, std::function<void()>([this]() { this->throw_if_canceled(); }));
} else { } else {
TreeSupport tree_support(*this, m_slicing_params); TreeSupport tree_support(*this, m_slicing_params);
tree_support.generate(); tree_support.generate();

File diff suppressed because it is too large Load diff

View file

@ -1,39 +0,0 @@
#ifndef slic3r_OrganicSupport_hpp
#define slic3r_OrganicSupport_hpp
#include "SupportCommon.hpp"
#include "TreeSupport.hpp"
namespace Slic3r
{
class PrintObject;
namespace FFFTreeSupport
{
class TreeModelVolumes;
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
void organic_draw_branches(
PrintObject &print_object,
TreeModelVolumes &volumes,
const TreeSupportSettings &config,
std::vector<SupportElements> &move_bounds,
// I/O:
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
InterfacePlacer &interface_placer,
// Output:
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage,
std::function<void()> throw_on_cancel);
} // namespace FFFTreeSupport
} // namespace Slic3r
#endif // slic3r_OrganicSupport_hpp

View file

@ -32,7 +32,7 @@
#include <cassert> #include <cassert>
namespace Slic3r::FFFSupport { namespace Slic3r {
// how much we extend support around the actual contact area // how much we extend support around the actual contact area
//FIXME this should be dependent on the nozzle diameter! //FIXME this should be dependent on the nozzle diameter!

View file

@ -12,8 +12,6 @@ namespace Slic3r {
class PrintObject; class PrintObject;
class SupportLayer; class SupportLayer;
namespace FFFSupport {
// Remove bridges from support contact areas. // Remove bridges from support contact areas.
// To be called if PrintObjectConfig::dont_support_bridges. // To be called if PrintObjectConfig::dont_support_bridges.
void remove_bridges_from_contacts( void remove_bridges_from_contacts(
@ -150,8 +148,6 @@ int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL fn_lo
return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal); return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal);
} }
} // namespace FFFSupport
} // namespace Slic3r } // namespace Slic3r
#endif /* slic3r_SupportCommon_hpp_ */ #endif /* slic3r_SupportCommon_hpp_ */

View file

@ -1,108 +0,0 @@
#if 1 //#ifdef SLIC3R_DEBUG
#include "../ClipperUtils.hpp"
#include "../SVG.hpp"
#include "../Layer.hpp"
#include "SupportLayer.hpp"
namespace Slic3r::FFFSupport {
const char* support_surface_type_to_color_name(const SupporLayerType surface_type)
{
switch (surface_type) {
case SupporLayerType::TopContact: return "rgb(255,0,0)"; // "red";
case SupporLayerType::TopInterface: return "rgb(0,255,0)"; // "green";
case SupporLayerType::Base: return "rgb(0,0,255)"; // "blue";
case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow
case SupporLayerType::BottomContact: return "rgb(255,0,255)"; // magenta
case SupporLayerType::RaftInterface: return "rgb(0,255,255)";
case SupporLayerType::RaftBase: return "rgb(128,128,128)";
case SupporLayerType::Unknown: return "rgb(128,0,0)"; // maroon
default: return "rgb(64,64,64)";
};
}
Point export_support_surface_type_legend_to_svg_box_size()
{
return Point(scale_(1.+10.*8.), scale_(3.));
}
void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos)
{
// 1st row
coord_t pos_x0 = pos(0) + scale_(1.);
coord_t pos_x = pos_x0;
coord_t pos_y = pos(1) + scale_(1.5);
coord_t step_x = scale_(10.);
svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(SupporLayerType::TopContact));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "top iface" , support_surface_type_to_color_name(SupporLayerType::TopInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "base" , support_surface_type_to_color_name(SupporLayerType::Base));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom iface" , support_surface_type_to_color_name(SupporLayerType::BottomInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(SupporLayerType::BottomContact));
// 2nd row
pos_x = pos_x0;
pos_y = pos(1)+scale_(2.8);
svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(SupporLayerType::RaftInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(SupporLayerType::RaftBase));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "unknown" , support_surface_type_to_color_name(SupporLayerType::Unknown));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "intermediate" , support_surface_type_to_color_name(SupporLayerType::Intermediate));
}
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, int n_layers)
{
BoundingBox bbox;
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency);
for (int i = 0; i < n_layers; ++ i)
svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
void export_print_z_polygons_and_extrusions_to_svg(
const char *path,
SupportGeneratorLayer ** const layers,
int n_layers,
SupportLayer &support_layer)
{
BoundingBox bbox;
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency);
for (int i = 0; i < n_layers; ++ i)
svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
Polygons polygons_support, polygons_interface;
support_layer.support_fills.polygons_covered_by_width(polygons_support, float(SCALED_EPSILON));
// support_layer.support_interface_fills.polygons_covered_by_width(polygons_interface, SCALED_EPSILON);
svg.draw(union_ex(polygons_support), "brown");
svg.draw(union_ex(polygons_interface), "black");
export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
} // namespace Slic3r
#endif /* SLIC3R_DEBUG */

View file

@ -1,18 +0,0 @@
#ifndef slic3r_SupportCommon_hpp_
#define slic3r_SupportCommon_hpp_
namespace Slic3r {
class SupportGeneratorLayer;
class SupportLayer;
namespace FFFSupport {
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers);
void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer);
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportCommon_hpp_ */

View file

@ -8,7 +8,7 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../Polygon.hpp" #include "../Polygon.hpp"
namespace Slic3r::FFFSupport { namespace Slic3r {
// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information // Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject. // about the support layer type than the final support layers stored in a PrintObject.

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,15 @@
#ifndef slic3r_SupportMaterial_hpp_ #ifndef slic3r_SupportMaterial_hpp_
#define slic3r_SupportMaterial_hpp_ #define slic3r_SupportMaterial_hpp_
#include "../Flow.hpp" #include "Flow.hpp"
#include "../PrintConfig.hpp" #include "PrintConfig.hpp"
#include "../Slicing.hpp" #include "Slicing.hpp"
#include "SupportLayer.hpp"
#include "SupportParameters.hpp"
namespace Slic3r { namespace Slic3r {
class PrintObject; class PrintObject;
class PrintConfig;
class PrintObjectConfig;
// This class manages raft and supports for a single PrintObject. // This class manages raft and supports for a single PrintObject.
// Instantiated by Slic3r::Print::Object->_support_material() // Instantiated by Slic3r::Print::Object->_support_material()
@ -18,6 +17,142 @@ class PrintObject;
// the parameters of the raft to determine the 1st layer height and thickness. // the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial class PrintObjectSupportMaterial
{ {
public:
// Support layer type to be used by MyLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum SupporLayerType {
sltUnknown = 0,
// Ratft base layer, to be printed with the support material.
sltRaftBase,
// Raft interface layer, to be printed with the support interface material.
sltRaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
sltBottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an sltBottomContact layer.
sltBottomInterface,
// Sparse base support layer, to be printed with a support material.
sltBase,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with sltTopContact layer.
sltTopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
sltTopContact,
// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
sltIntermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the MyLayer is aware of the bridging flow and the interface gaps between the object and the support.
class MyLayer
{
public:
void reset() {
*this = MyLayer();
}
bool operator==(const MyLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const MyLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
} else if (print_z == layer2.print_z) {
if (height > layer2.height)
return true;
else if (height == layer2.height) {
// Bridging layers first.
return bridging && ! layer2.bridging;
} else
return false;
} else
return false;
}
void merge(MyLayer &&rhs) {
// The union_() does not support move semantic yet, but maybe one day it will.
this->polygons = union_(this->polygons, std::move(rhs.polygons));
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
if (! dst || dst->empty())
dst = std::move(src);
else if (src && ! src->empty())
*dst = union_(*dst, std::move(*src));
};
merge(this->contact_polygons, rhs.contact_polygons);
merge(this->overhang_polygons, rhs.overhang_polygons);
merge(this->enforcer_polygons, rhs.enforcer_polygons);
rhs.reset();
}
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
coordf_t bottom_print_z() const { return print_z - height; }
// To sort the extremes of top / bottom interface layers.
coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; }
SupporLayerType layer_type { sltUnknown };
// Z used for printing, in unscaled coordinates.
coordf_t print_z { 0 };
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z { 0 };
// Layer height in unscaled coordinates.
coordf_t height { 0 };
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_above { size_t(-1) };
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_below { size_t(-1) };
// Use a bridging flow when printing this support layer.
bool bridging { false };
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only.
std::unique_ptr<Polygons> contact_polygons;
std::unique_ptr<Polygons> overhang_polygons;
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
std::unique_ptr<Polygons> enforcer_polygons;
};
struct SupportParams {
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t support_expansion;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
};
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
typedef std::deque<MyLayer> MyLayerStorage;
typedef std::vector<MyLayer*> MyLayersPtr;
public: public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params); PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
@ -26,8 +161,8 @@ public:
// Has any support? // Has any support?
bool has_support() const { return m_object_config->enable_support.value || m_object_config->enforce_support_layers; } bool has_support() const { return m_object_config->enable_support.value || m_object_config->enforce_support_layers; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_on_build_plate_only.value; } bool build_plate_only() const { return this->has_support() && m_object_config->support_on_build_plate_only.value; }
// BBS
bool synchronize_layers() const { return m_slicing_params.soluble_interface && m_print_config->independent_support_layer_height.value; } bool synchronize_layers() const { return /*m_slicing_params.soluble_interface && */!m_print_config->independent_support_layer_height.value; }
bool has_contact_loops() const { return m_object_config->support_interface_loop_pattern.value; } bool has_contact_loops() const { return m_object_config->support_interface_loop_pattern.value; }
// Generate support material for the object. // Generate support material for the object.
@ -36,47 +171,63 @@ public:
void generate(PrintObject &object); void generate(PrintObject &object);
private: private:
using SupportGeneratorLayersPtr = FFFSupport::SupportGeneratorLayersPtr;
using SupportGeneratorLayerStorage = FFFSupport::SupportGeneratorLayerStorage;
using SupportParameters = FFFSupport::SupportParameters;
std::vector<Polygons> buildplate_covered(const PrintObject &object) const; std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
// Generate top contact layers supporting overhangs. // Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. // For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object. // If supports over bed surface only are requested, don't generate contact layers over an object.
SupportGeneratorLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const; MyLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, MyLayerStorage &layer_storage) const;
// Generate bottom contact layers supporting the top contact layers. // Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object, // For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle. // otherwise set the layer height to a bridging flow of a support interface nozzle.
SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas( MyLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered, const PrintObject &object, const MyLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
SupportGeneratorLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const; MyLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them. // Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const; void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces. // Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
SupportGeneratorLayersPtr raft_and_intermediate_support_layers( MyLayersPtr raft_and_intermediate_support_layers(
const PrintObject &object, const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts, const MyLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts, const MyLayersPtr &top_contacts,
SupportGeneratorLayerStorage &layer_storage) const; MyLayerStorage &layer_storage) const;
// Fill in the base layers with polygons. // Fill in the base layers with polygons.
void generate_base_layers( void generate_base_layers(
const PrintObject &object, const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts, const MyLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts, const MyLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers, MyLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const; const std::vector<Polygons> &layer_support_areas) const;
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
MyLayersPtr generate_raft_base(
const PrintObject &object,
const MyLayersPtr &top_contacts,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers,
const MyLayersPtr &base_layers,
MyLayerStorage &layer_storage) const;
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
std::pair<MyLayersPtr, MyLayersPtr> generate_interface_layers(
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
MyLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between // Trim support layers by an object to leave a defined gap between
// the support volume and the object. // the support volume and the object.
void trim_support_layers_by_object( void trim_support_layers_by_object(
const PrintObject &object, const PrintObject &object,
SupportGeneratorLayersPtr &support_layers, MyLayersPtr &support_layers,
const coordf_t gap_extra_above, const coordf_t gap_extra_above,
const coordf_t gap_extra_below, const coordf_t gap_extra_below,
const coordf_t gap_xy) const; const coordf_t gap_xy) const;
@ -86,14 +237,25 @@ private:
void clip_with_shape(); void clip_with_shape();
*/ */
// Produce the actual G-code.
void generate_toolpaths(
SupportLayerPtrs &support_layers,
const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
const MyLayersPtr &intermediate_layers,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers) const;
// Following objects are not owned by SupportMaterial class. // Following objects are not owned by SupportMaterial class.
const PrintObject *m_object;
const PrintConfig *m_print_config; const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config; const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator, // Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc. // carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params; SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions. // Various precomputed support parameters to be shared with external functions.
SupportParameters m_support_params; SupportParams m_support_params;
}; };
} // namespace Slic3r } // namespace Slic3r

View file

@ -1,144 +0,0 @@
#include "../Print.hpp"
#include "../PrintConfig.hpp"
#include "../Slicing.hpp"
#include "SupportParameters.hpp"
namespace Slic3r::FFFSupport {
SupportParameters::SupportParameters(const PrintObject &object)
{
const PrintConfig &print_config = object.print()->config();
const PrintObjectConfig &object_config = object.config();
const SlicingParameters &slicing_params = object.slicing_parameters();
this->soluble_interface = slicing_params.soluble_interface;
this->soluble_interface_non_soluble_base =
// Zero z-gap between the overhangs and the support interface.
slicing_params.soluble_interface &&
// Interface extruder soluble.
object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) &&
// Base extruder: Either "print with active extruder" not soluble.
(object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1));
{
int num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value);
int num_bottom_interface_layers = object_config.support_interface_bottom_layers < 0 ?
num_top_interface_layers : object_config.support_interface_bottom_layers;
this->has_top_contacts = num_top_interface_layers > 0;
this->has_bottom_contacts = num_bottom_interface_layers > 0;
this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0;
this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0;
if (this->soluble_interface_non_soluble_base) {
// Try to support soluble dense interfaces with non-soluble dense interfaces.
this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2));
this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2));
} else {
this->num_top_base_interface_layers = 0;
this->num_bottom_base_interface_layers = 0;
}
}
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
this->raft_interface_flow = support_material_interface_flow;
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
this->support_layer_height_min = scaled<coord_t>(0.01);
for (auto lh : print_config.min_layer_height.values)
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh));
for (auto layer : object.layers())
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height));
if (object_config.support_interface_top_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->support_material_interface_flow = this->support_material_flow;
}
// Evaluate the XY gap between the object outer perimeters and the support structures.
// Evaluate the XY gap between the object outer perimeters and the support structures.
coordf_t external_perimeter_width = 0.;
coordf_t bridge_flow_ratio = 0;
for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = object.printing_region(region_id);
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width()));
bridge_flow_ratio += region.config().bridge_flow;
}
this->gap_xy = object_config.support_object_xy_distance;//.get_abs_value(external_perimeter_width);
bridge_flow_ratio /= object.num_printing_regions();
this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ?
this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter());
this->can_merge_support_regions = object_config.support_filament.value == object_config.support_interface_filament.value;
if (!this->can_merge_support_regions && (object_config.support_filament.value == 0 || object_config.support_interface_filament.value == 0)) {
// One of the support extruders is of "don't care" type.
auto object_extruders = object.object_extruders();
if (object_extruders.size() == 1 &&
*object_extruders.begin() == std::max<unsigned int>(object_config.support_filament.value, object_config.support_interface_filament.value))
// Object is printed with the same extruder as the support.
this->can_merge_support_regions = true;
}
double interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing();
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
double raft_interface_spacing = object_config.support_interface_spacing.value + this->raft_interface_flow.spacing();
this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing);
double support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing();
this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing);
if (object_config.support_interface_top_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->interface_density = this->support_density;
}
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
this->with_sheath = false;//object_config.support_material_with_sheath;
this->base_fill_pattern =
support_pattern == smpHoneycomb ? ipHoneycomb :
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase;
this->contact_fill_pattern =
(object_config.support_interface_pattern == smipAuto && slicing_params.soluble_interface) ||
object_config.support_interface_pattern == smipConcentric ?
ipConcentric :
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value));
this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.));
this->raft_angle_1st_layer = 0.f;
this->raft_angle_base = 0.f;
this->raft_angle_interface = 0.f;
if (slicing_params.base_raft_layers > 1) {
assert(slicing_params.raft_layers() >= 4);
// There are all raft layer types (1st layer, base, interface & contact layers) available.
this->raft_angle_1st_layer = this->interface_angle;
this->raft_angle_base = this->base_angle;
this->raft_angle_interface = this->interface_angle;
if ((slicing_params.interface_raft_layers & 1) == 0)
// Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface.
this->raft_angle_interface += float(0.5 * M_PI);
} else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) {
assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3);
// 1st layer, interface & contact layers available.
this->raft_angle_1st_layer = this->base_angle;
this->raft_angle_interface = this->interface_angle + 0.5 * M_PI;
} else if (slicing_params.interface_raft_layers == 1) {
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 1);
assert(slicing_params.raft_layers() == 1);
this->raft_angle_1st_layer = float(0.5 * M_PI);
this->raft_angle_interface = this->raft_angle_1st_layer;
} else {
// No raft.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 0);
assert(slicing_params.raft_layers() == 0);
}
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI;
}
} // namespace Slic3r

View file

@ -9,10 +9,142 @@ namespace Slic3r {
class PrintObject; class PrintObject;
enum InfillPattern : int; enum InfillPattern : int;
namespace FFFSupport {
struct SupportParameters { struct SupportParameters {
SupportParameters(const PrintObject &object); SupportParameters(const PrintObject &object)
{
const PrintConfig &print_config = object.print()->config();
const PrintObjectConfig &object_config = object.config();
const SlicingParameters &slicing_params = object.slicing_parameters();
this->soluble_interface = slicing_params.soluble_interface;
this->soluble_interface_non_soluble_base =
// Zero z-gap between the overhangs and the support interface.
slicing_params.soluble_interface &&
// Interface extruder soluble.
object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) &&
// Base extruder: Either "print with active extruder" not soluble.
(object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1));
{
int num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value);
int num_bottom_interface_layers = object_config.support_interface_bottom_layers < 0 ?
num_top_interface_layers : object_config.support_interface_bottom_layers;
this->has_top_contacts = num_top_interface_layers > 0;
this->has_bottom_contacts = num_bottom_interface_layers > 0;
this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0;
this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0;
if (this->soluble_interface_non_soluble_base) {
// Try to support soluble dense interfaces with non-soluble dense interfaces.
this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2));
this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2));
} else {
this->num_top_base_interface_layers = 0;
this->num_bottom_base_interface_layers = 0;
}
}
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
this->raft_interface_flow = support_material_interface_flow;
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
this->support_layer_height_min = scaled<coord_t>(0.01);
for (auto lh : print_config.min_layer_height.values)
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh));
for (auto layer : object.layers())
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height));
if (object_config.support_interface_top_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->support_material_interface_flow = this->support_material_flow;
}
// Evaluate the XY gap between the object outer perimeters and the support structures.
// Evaluate the XY gap between the object outer perimeters and the support structures.
coordf_t external_perimeter_width = 0.;
coordf_t bridge_flow_ratio = 0;
for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = object.printing_region(region_id);
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width()));
bridge_flow_ratio += region.config().bridge_flow;
}
this->gap_xy = object_config.support_object_xy_distance;//.get_abs_value(external_perimeter_width);
bridge_flow_ratio /= object.num_printing_regions();
this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ?
this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter());
this->can_merge_support_regions = object_config.support_filament.value == object_config.support_interface_filament.value;
if (!this->can_merge_support_regions && (object_config.support_filament.value == 0 || object_config.support_interface_filament.value == 0)) {
// One of the support extruders is of "don't care" type.
auto object_extruders = object.object_extruders();
if (object_extruders.size() == 1 &&
*object_extruders.begin() == std::max<unsigned int>(object_config.support_filament.value, object_config.support_interface_filament.value))
// Object is printed with the same extruder as the support.
this->can_merge_support_regions = true;
}
double interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing();
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
double raft_interface_spacing = object_config.support_interface_spacing.value + this->raft_interface_flow.spacing();
this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing);
double support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing();
this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing);
if (object_config.support_interface_top_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->interface_density = this->support_density;
}
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
this->with_sheath = false;//object_config.support_material_with_sheath;
this->base_fill_pattern =
support_pattern == smpHoneycomb ? ipHoneycomb :
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase;
this->contact_fill_pattern =
(object_config.support_interface_pattern == smipAuto && slicing_params.soluble_interface) ||
object_config.support_interface_pattern == smipConcentric ?
ipConcentric :
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value));
this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.));
this->raft_angle_1st_layer = 0.f;
this->raft_angle_base = 0.f;
this->raft_angle_interface = 0.f;
if (slicing_params.base_raft_layers > 1) {
assert(slicing_params.raft_layers() >= 4);
// There are all raft layer types (1st layer, base, interface & contact layers) available.
this->raft_angle_1st_layer = this->interface_angle;
this->raft_angle_base = this->base_angle;
this->raft_angle_interface = this->interface_angle;
if ((slicing_params.interface_raft_layers & 1) == 0)
// Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface.
this->raft_angle_interface += float(0.5 * M_PI);
} else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) {
assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3);
// 1st layer, interface & contact layers available.
this->raft_angle_1st_layer = this->base_angle;
this->raft_angle_interface = this->interface_angle + 0.5 * M_PI;
} else if (slicing_params.interface_raft_layers == 1) {
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 1);
assert(slicing_params.raft_layers() == 1);
this->raft_angle_1st_layer = float(0.5 * M_PI);
this->raft_angle_interface = this->raft_angle_1st_layer;
} else {
// No raft.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 0);
assert(slicing_params.raft_layers() == 0);
}
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI;
}
// Both top / bottom contacts and interfaces are soluble. // Both top / bottom contacts and interfaces are soluble.
bool soluble_interface; bool soluble_interface;
@ -89,8 +221,6 @@ struct SupportParameters {
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); } { return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
}; };
} // namespace FFFSupport
} // namespace Slic3r } // namespace Slic3r
#endif /* slic3r_SupportParameters_hpp_ */ #endif /* slic3r_SupportParameters_hpp_ */

View file

@ -26,7 +26,7 @@
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <tbb/task_group.h> #include <tbb/task_group.h>
namespace Slic3r::FFFTreeSupport namespace Slic3r::TreeSupport3D
{ {
using namespace std::literals; using namespace std::literals;
@ -871,4 +871,4 @@ std::vector<std::pair<TreeModelVolumes::RadiusLayerPair, std::reference_wrapper<
return out; return out;
} }
} // namespace Slic3r::FFFTreeSupport } // namespace Slic3r::TreeSupport3D

View file

@ -26,7 +26,7 @@ namespace Slic3r
class BuildVolume; class BuildVolume;
class PrintObject; class PrintObject;
namespace FFFTreeSupport namespace TreeSupport3D
{ {
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5; static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
@ -548,7 +548,7 @@ private:
#endif // SLIC3R_TREESUPPORTS_PROGRESS #endif // SLIC3R_TREESUPPORTS_PROGRESS
}; };
} // namespace FFFTreeSupport } // namespace TreeSupport3D
} // namespace Slic3r } // namespace Slic3r
#endif //slic3r_TreeModelVolumes_hpp #endif //slic3r_TreeModelVolumes_hpp

File diff suppressed because it is too large Load diff

View file

@ -1,299 +1,511 @@
// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine. #ifndef TREESUPPORT_H
// Original source of Thomas Rahm's tree supports: #define TREESUPPORT_H
// https://github.com/ThomasRahm/CuraEngine
//
// Original CuraEngine copyright:
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef slic3r_TreeSupport_hpp #include <forward_list>
#define slic3r_TreeSupport_hpp #include <unordered_set>
#include "ExPolygon.hpp"
#include "Point.hpp"
#include "Slicing.hpp"
#include "MinimumSpanningTree.hpp"
#include "tbb/concurrent_unordered_map.h"
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Fill/Lightning/Generator.hpp"
#include "SupportLayer.hpp" #ifndef SQ
#include "TreeModelVolumes.hpp" #define SQ(x) ((x)*(x))
#include "TreeSupportCommon.hpp" #endif
#include "../BoundingBox.hpp"
#include "../Point.hpp"
#include "../Utils.hpp"
#include <boost/container/small_vector.hpp>
// #define TREE_SUPPORT_SHOW_ERRORS
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
// The various stages of the process can be weighted differently in the progress bar.
// These weights are obtained experimentally using a small sample size. Sensible weights can differ drastically based on the assumed default settings and model.
#define TREE_PROGRESS_TOTAL 10000
#define TREE_PROGRESS_PRECALC_COLL TREE_PROGRESS_TOTAL * 0.1
#define TREE_PROGRESS_PRECALC_AVO TREE_PROGRESS_TOTAL * 0.4
#define TREE_PROGRESS_GENERATE_NODES TREE_PROGRESS_TOTAL * 0.1
#define TREE_PROGRESS_AREA_CALC TREE_PROGRESS_TOTAL * 0.3
#define TREE_PROGRESS_DRAW_AREAS TREE_PROGRESS_TOTAL * 0.1
#define TREE_PROGRESS_GENERATE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#define TREE_PROGRESS_SMOOTH_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#define TREE_PROGRESS_FINALIZE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#endif // SLIC3R_TREESUPPORTS_PROGRESS
namespace Slic3r namespace Slic3r
{ {
// Forward declarations
class Print;
class PrintObject; class PrintObject;
struct SlicingParameters; class TreeSupport;
class SupportLayer;
namespace FFFTreeSupport struct LayerHeightData
{ {
coordf_t print_z = 0;
coordf_t height = 0;
size_t next_layer_nr = 0;
LayerHeightData() = default;
LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {}
};
// The number of vertices in each circle. struct TreeNode {
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25; Vec3f pos;
std::vector<int> children; // index of children in the storing vector
struct AreaIncreaseSettings std::vector<int> parents; // index of parents in the storing vector
{ TreeNode(Point pt, float z) {
AreaIncreaseSettings( pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z };
TreeModelVolumes::AvoidanceType type = TreeModelVolumes::AvoidanceType::Fast, coord_t increase_speed = 0,
bool increase_radius = false, bool no_error = false, bool use_min_distance = false, bool move = false) :
increase_speed{ increase_speed }, type{ type }, increase_radius{ increase_radius }, no_error{ no_error }, use_min_distance{ use_min_distance }, move{ move } {}
coord_t increase_speed;
// Packing for smaller memory footprint of SupportElementState && SupportElementMerging
TreeModelVolumes::AvoidanceType type;
bool increase_radius : 1;
bool no_error : 1;
bool use_min_distance : 1;
bool move : 1;
bool operator==(const AreaIncreaseSettings& other) const
{
return type == other.type &&
increase_speed == other.increase_speed &&
increase_radius == other.increase_radius &&
no_error == other.no_error &&
use_min_distance == other.use_min_distance &&
move == other.move;
} }
}; };
#define TREE_SUPPORTS_TRACK_LOST /*!
* \brief Lazily generates tree guidance volumes.
*
* \warning This class is not currently thread-safe and should not be accessed in OpenMP blocks
*/
class TreeSupportData
{
public:
TreeSupportData() = default;
/*!
* \brief Construct the TreeSupportData object
*
* \param xy_distance The required clearance between the model and the
* tree branches.
* \param max_move The maximum allowable movement between nodes on
* adjacent layers
* \param radius_sample_resolution Sample size used to round requested node radii.
* \param collision_resolution
*/
TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution);
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided. TreeSupportData(TreeSupportData&&) = default;
struct SupportElementStateBits { TreeSupportData& operator=(TreeSupportData&&) = default;
SupportElementStateBits() :
to_buildplate(false), TreeSupportData(const TreeSupportData&) = delete;
to_model_gracious(false), TreeSupportData& operator=(const TreeSupportData&) = delete;
use_min_xy_dist(false),
supports_roof(false), /*!
can_use_safe_radius(false), * \brief Creates the areas that have to be avoided by the tree's branches.
skip_ovalisation(false), *
#ifdef TREE_SUPPORTS_TRACK_LOST * The result is a 2D area that would cause nodes of radius \p radius to
lost(false), * collide with the model.
verylost(false), *
#endif // TREE_SUPPORTS_TRACK_LOST * \param radius The radius of the node of interest
deleted(false), * \param layer The layer of interest
marked(false) * \return Polygons object
*/
const ExPolygons& get_collision(coordf_t radius, size_t layer_idx) const;
/*!
* \brief Creates the areas that have to be avoided by the tree's branches
* in order to reach the build plate.
*
* The result is a 2D area that would cause nodes of radius \p radius to
* collide with the model or be unable to reach the build platform.
*
* The input collision areas are inset by the maximum move distance and
* propagated upwards.
*
* \param radius The radius of the node of interest
* \param layer The layer of interest
* \return Polygons object
*/
const ExPolygons& get_avoidance(coordf_t radius, size_t layer_idx, int recursions=0) const;
Polygons get_contours(size_t layer_nr) const;
Polygons get_contours_with_holes(size_t layer_nr) const;
std::vector<LayerHeightData> layer_heights;
std::vector<TreeNode> tree_nodes;
private:
/*!
* \brief Convenience typedef for the keys to the caches
*/
struct RadiusLayerPair {
coordf_t radius;
size_t layer_nr;
int recursions;
};
struct RadiusLayerPairEquality {
constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const {
return _Left.radius == _Right.radius && _Left.layer_nr == _Right.layer_nr;
}
};
struct RadiusLayerPairHash {
size_t operator()(const RadiusLayerPair& elem) const {
return std::hash<coord_t>()(elem.radius) ^ std::hash<coord_t>()(elem.layer_nr * 7919);
}
};
/*!
* \brief Round \p radius upwards to a multiple of m_radius_sample_resolution
*
* \param radius The radius of the node of interest
*/
coordf_t ceil_radius(coordf_t radius) const;
/*!
* \brief Calculate the collision areas at the radius and layer indicated
* by \p key.
*
* \param key The radius and layer of the node of interest
*/
const ExPolygons& calculate_collision(const RadiusLayerPair& key) const;
/*!
* \brief Calculate the avoidance areas at the radius and layer indicated
* by \p key.
*
* \param key The radius and layer of the node of interest
*/
const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const;
public:
bool is_slim = false;
/*!
* \brief The required clearance between the model and the tree branches
*/
coordf_t m_xy_distance;
/*!
* \brief The maximum distance that the centrepoint of a tree branch may
* move in consequtive layers
*/
coordf_t m_max_move;
/*!
* \brief Sample resolution for radius values.
*
* The radius will be rounded (upwards) to multiples of this value before
* calculations are done when collision, avoidance and internal model
* Polygons are requested.
*/
coordf_t m_radius_sample_resolution;
/*!
* \brief Storage for layer outlines of the meshes.
*/
std::vector<ExPolygons> m_layer_outlines;
// union contours of all layers below
std::vector<ExPolygons> m_layer_outlines_below;
/*!
* \brief Caches for the collision, avoidance and internal model polygons
* at given radius and layer indices.
*
* These are mutable to allow modification from const function. This is
* generally considered OK as the functions are still logically const
* (ie there is no difference in behaviour for the user betweeen
* calculating the values each time vs caching the results).
*
* coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for.
* So we change to tbb::concurrent_unordered_map
*/
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_collision_cache;
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_avoidance_cache;
friend TreeSupport;
};
struct LineHash {
size_t operator()(const Line& line) const {
return (std::hash<coord_t>()(line.a(0)) ^ std::hash<coord_t>()(line.b(1))) * 102 +
(std::hash<coord_t>()(line.a(1)) ^ std::hash<coord_t>()(line.b(0))) * 10222;
}
};
/*!
* \brief Generates a tree structure to support your models.
*/
class TreeSupport
{
public:
/*!
* \brief Creates an instance of the tree support generator.
*
* \param storage The data storage to get global settings from.
*/
TreeSupport(PrintObject& object, const SlicingParameters &slicing_params);
/*!
* \brief Create the areas that need support.
*
* These areas are stored inside the given SliceDataStorage object.
* \param storage The data storage where the mesh data is gotten from and
* where the resulting support areas are stored.
*/
void generate();
void detect_overhangs(bool detect_first_sharp_tail_only=false);
enum NodeType {
eCircle,
eSquare,
ePolygon
};
/*!
* \brief Represents the metadata of a node in the tree.
*/
struct Node
{
static constexpr Node* NO_PARENT = nullptr;
Node()
: distance_to_top(0)
, position(Point(0, 0))
, obj_layer_nr(0)
, support_roof_layers_below(0)
, support_floor_layers_above(0)
, to_buildplate(true)
, parent(nullptr)
, print_z(0.0)
, height(0.0)
{} {}
/*! // when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height;
* \brief The element trys to reach the buildplate Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
*/ coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0)
bool to_buildplate : 1; : distance_to_top(distance_to_top)
, position(position)
, obj_layer_nr(obj_layer_nr)
, support_roof_layers_below(support_roof_layers_below)
, support_floor_layers_above(0)
, to_buildplate(to_buildplate)
, parent(parent)
, print_z(print_z_)
, height(height_)
, dist_mm_to_top(dist_mm_to_top_)
{
if (parent) {
type = parent->type;
overhang = parent->overhang;
if (dist_mm_to_top==0)
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
parent->child = this;
for (auto& neighbor : parent->merged_neighbours)
neighbor->child = this;
}
}
/*! #ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node.
* \brief Will the branch be able to rest completely on a flat surface, be it buildplate or model ? ~Node()
*/ {
bool to_model_gracious : 1; parent = nullptr;
merged_neighbours.clear();
}
#endif // DEBUG
/*! /*!
* \brief Whether the min_xy_distance can be used to get avoidance or similar. Will only be true if support_xy_overrides_z=Z overrides X/Y. * \brief The number of layers to go to the top of this branch.
*/ * Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded.
bool use_min_xy_dist : 1; */
int distance_to_top;
coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm
/*! /*!
* \brief True if this Element or any parent (element above) provides support to a support roof. * \brief The position of this node on the layer.
*/ */
bool supports_roof : 1; Point position;
Point movement; // movement towards neighbor center or outline
mutable double radius = 0.0;
mutable double max_move_dist = 0.0;
NodeType type = eCircle;
bool is_merged = false; // this node is generated by merging upper nodes
bool is_corner = false;
bool is_processed = false;
const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area
/*! /*!
* \brief An influence area is considered safe when it can use the holefree avoidance <=> It will not have to encounter holes on its way downward. * \brief The direction of the skin lines above the tip of the branch.
*/ *
bool can_use_safe_radius : 1; * This determines in which direction we should reduce the width of the
* branch.
*/
bool skin_direction;
/*! /*!
* \brief Skip the ovalisation to parent and children when generating the final circles. * \brief The number of support roof layers below this one.
*/ *
bool skip_ovalisation : 1; * When a contact point is created, it is determined whether the mesh
* needs to be supported with support roof or not, since that is a
* per-mesh setting. This is stored in this variable in order to track
* how far we need to extend that support roof downwards.
*/
int support_roof_layers_below;
int support_floor_layers_above;
int obj_layer_nr;
#ifdef TREE_SUPPORTS_TRACK_LOST /*!
// Likely a lost branch, debugging information. * \brief Whether to try to go towards the build plate.
bool lost : 1; *
bool verylost : 1; * If the node is inside the collision areas, it has no choice but to go
#endif // TREE_SUPPORTS_TRACK_LOST * towards the model. If it is not inside the collision areas, it must
* go towards the build plate to prevent a scar on the surface.
*/
bool to_buildplate;
// Not valid anymore, to be deleted. /*!
bool deleted : 1; * \brief The originating node for this one, one layer higher.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
Node *parent;
Node *child = nullptr;
// General purpose flag marking a visited element. /*!
bool marked : 1; * \brief All neighbours (on the same layer) that where merged into this node.
}; *
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
std::list<Node*> merged_neighbours;
struct SupportElementState : public SupportElementStateBits coordf_t print_z;
{ coordf_t height;
/*!
* \brief The layer this support elements wants reach
*/
LayerIndex target_height;
/*! bool operator==(const Node& other) const
* \brief The position this support elements wants to support on layer=target_height {
*/ return position == other.position;
Point target_position; }
};
/*! struct SupportParams
* \brief The next position this support elements wants to reach. NOTE: This is mainly a suggestion regarding direction inside the influence area.
*/
Point next_position;
/*!
* \brief The next height this support elements wants to reach
*/
LayerIndex layer_idx;
/*!
* \brief The Effective distance to top of this element regarding radius increases and collision calculations.
*/
uint32_t effective_radius_height;
/*!
* \brief The amount of layers this element is below the topmost layer of this branch.
*/
uint32_t distance_to_top;
/*!
* \brief The resulting center point around which a circle will be drawn later.
* Will be set by setPointsOnAreas
*/
Point result_on_layer { std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
/*!
* \brief The amount of extra radius we got from merging branches that could have reached the buildplate, but merged with ones that can not.
*/
coord_t increased_to_model_radius; // how much to model we increased only relevant for merging
/*!
* \brief Counter about the times the elephant foot was increased. Can be fractions for merge reasons.
*/
double elephant_foot_increases;
/*!
* \brief The element tries to not move until this dtt is reached, is set to 0 if the element had to move.
*/
uint32_t dont_move_until;
/*!
* \brief Settings used to increase the influence area to its current state.
*/
AreaIncreaseSettings last_area_increase;
/*!
* \brief Amount of roof layers that were not yet added, because the branch needed to move.
*/
uint32_t missing_roof_layers;
// called by increase_single_area() and increaseAreas()
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState &src)
{ {
SupportElementState dst{ src }; Flow first_layer_flow;
++ dst.distance_to_top; Flow support_material_flow;
-- dst.layer_idx; Flow support_material_interface_flow;
// set to invalid as we are a new node on a new layer Flow support_material_bottom_interface_flow;
dst.result_on_layer_reset(); coordf_t support_extrusion_width;
dst.skip_ovalisation = false; // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
return dst; bool can_merge_support_regions;
}
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; } coordf_t support_layer_height_min;
}; // coordf_t support_layer_height_max;
/*! coordf_t gap_xy;
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
* \param elem[in] The SupportElement one wants to know the effectiveDTT
* \return The Effective DTT.
*/
[[nodiscard]] inline size_t getEffectiveDTT(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return elem.effective_radius_height < settings.increase_radius_until_layer ?
(elem.distance_to_top < settings.increase_radius_until_layer ? elem.distance_to_top : settings.increase_radius_until_layer) :
elem.effective_radius_height;
}
/*! float base_angle;
* \brief Get the Radius, that this element will have. float interface_angle;
* \param elem[in] The Element. coordf_t interface_spacing;
* \return The radius the element has. coordf_t interface_density;
*/ coordf_t support_spacing;
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElementState &elem) coordf_t support_density;
{
return settings.getRadius(getEffectiveDTT(settings, elem), elem.elephant_foot_increases);
}
/*! InfillPattern base_fill_pattern;
* \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model. InfillPattern interface_fill_pattern;
* \param elem[in] The Element. InfillPattern contact_fill_pattern;
* \return The collision radius the element has. bool with_sheath;
*/ const double thresh_big_overhang = SQ(scale_(10));
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElementState &elem) };
{
return settings.getRadius(elem.effective_radius_height, elem.elephant_foot_increases);
}
struct SupportElement int avg_node_per_layer = 0;
{ float nodes_angle = 0;
using ParentIndices = bool has_overhangs = false;
#ifdef NDEBUG bool has_sharp_tails = false;
// To reduce memory allocation in release mode. bool has_cantilever = false;
boost::container::small_vector<int32_t, 4>; double max_cantilever_dist = 0;
#else // NDEBUG SupportType support_type;
// To ease debugging. SupportMaterialStyle support_style;
std::vector<int32_t>;
#endif // NDEBUG
// SupportElement(const SupportElementState &state) : SupportElementState(state) {} std::unique_ptr<FillLightning::Generator> generator;
SupportElement(const SupportElementState &state, Polygons &&influence_area) : state(state), influence_area(std::move(influence_area)) {} std::unordered_map<double, size_t> printZ_to_lightninglayer;
SupportElement(const SupportElementState &state, ParentIndices &&parents, Polygons &&influence_area) : private:
state(state), parents(std::move(parents)), influence_area(std::move(influence_area)) {} /*!
* \brief Generator for model collision, avoidance and internal guide volumes
*
* Lazily computes volumes as needed.
* \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks
*/
std::shared_ptr<TreeSupportData> m_ts_data;
PrintObject *m_object;
const PrintObjectConfig *m_object_config;
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParams m_support_params;
size_t m_raft_layers = 0;
size_t m_highest_overhang_layer = 0;
std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees;
std::vector< std::unordered_map<Line, bool, LineHash>> m_mst_line_x_layer_contour_caches;
coordf_t MAX_BRANCH_RADIUS = 10.0;
coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
coordf_t MIN_BRANCH_RADIUS = 0.5;
float tree_support_branch_diameter_angle = 5.0;
bool is_strong = false;
bool is_slim = false;
bool with_infill = false;
SupportElementState state;
/*! /*!
* \brief All elements in the layer above the current one that are supported by this element * \brief Polygons representing the limits of the printable area of the
* machine
*/ */
ParentIndices parents; ExPolygon m_machine_border;
/*! /*!
* \brief The resulting influence area. * \brief Draws circles around each node of the tree into the final support.
* Will only be set in the results of createLayerPathing, and will be nullptr inside! *
* This also handles the areas that have to become support roof, support
* bottom, the Z distances, etc.
*
* \param storage[in, out] The settings storage to get settings from and to
* save the resulting support polygons to.
* \param contact_nodes The nodes to draw as support.
*/ */
Polygons influence_area; void draw_circles(const std::vector<std::vector<Node*>>& contact_nodes);
/*!
* \brief Drops down the nodes of the tree support towards the build plate.
*
* This is where the cleverness of tree support comes in: The nodes stay on
* their 2D layers but on the next layer they are slightly shifted. This
* causes them to move towards each other as they are copied to lower layers
* which ultimately results in a 3D tree.
*
* \param contact_nodes[in, out] The nodes in the space that need to be
* dropped down. The nodes are dropped to lower layers inside the same
* vector of layers.
*/
void drop_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes);
/*! BBS: MusangKing: maximum layer height
* \brief Optimize the generation of tree support by pre-planning the layer_heights
*
*/
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes);
/*!
* \brief Creates points where support contacts the model.
*
* A set of points is created for each layer.
* \param mesh The mesh to get the overhang areas to support of.
* \param contact_nodes[out] A vector of mappings from contact points to
* their tree nodes.
* \param collision_areas For every layer, the areas where a generated
* contact point would immediately collide with the model due to the X/Y
* distance.
* \return For each layer, a list of points where the tree should connect
* with the model.
*/
void generate_contact_points(std::vector<std::vector<Node*>>& contact_nodes);
/*!
* \brief Add a node to the next layer.
*
* If a node is already at that position in the layer, the nodes are merged.
*/
void insert_dropped_node(std::vector<Node*>& nodes_layer, Node* node);
void create_tree_support_layers();
void generate_toolpaths();
Polygons spanning_tree_to_polygon(const std::vector<MinimumSpanningTree>& spanning_trees, Polygons layer_contours, int layer_nr);
Polygons contact_nodes_to_polygon(const std::vector<Node*>& contact_nodes, Polygons layer_contours, int layer_nr, std::vector<double>& radiis, std::vector<bool>& is_interface);
coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor);
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor);
// similar to SupportMaterial::trim_support_layers_by_object
Polygons get_trim_support_regions(
const PrintObject& object,
SupportLayer* support_layer_ptr,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy);
}; };
using SupportElements = std::deque<SupportElement>;
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
{
return support_element_radius(settings, elem.state);
} }
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElement &elem) #endif /* TREESUPPORT_H */
{
return support_element_collision_radius(settings, elem.state);
}
} // namespace FFFTreeSupport
void fff_tree_support_generate(PrintObject &print_object, std::function<void()> throw_on_cancel = []{});
} // namespace Slic3r
#endif /* slic3r_TreeSupport_hpp */

File diff suppressed because it is too large Load diff

View file

@ -9,13 +9,17 @@
#ifndef slic3r_TreeSupport_hpp #ifndef slic3r_TreeSupport_hpp
#define slic3r_TreeSupport_hpp #define slic3r_TreeSupport_hpp
#include <boost/container/small_vector.hpp> #include "SupportLayer.hpp"
#include "../Point.hpp"
#include "../BoundingBox.hpp"
#include "../Utils.hpp"
#include "TreeModelVolumes.hpp" #include "TreeModelVolumes.hpp"
#include "TreeSupportCommon.hpp" #include "TreeSupportCommon.hpp"
#include "../BoundingBox.hpp"
#include "../Point.hpp"
#include "../Utils.hpp"
#include <boost/container/small_vector.hpp>
// #define TREE_SUPPORT_SHOW_ERRORS // #define TREE_SUPPORT_SHOW_ERRORS
#ifdef SLIC3R_TREESUPPORTS_PROGRESS #ifdef SLIC3R_TREESUPPORTS_PROGRESS
@ -36,11 +40,9 @@ namespace Slic3r
{ {
// Forward declarations // Forward declarations
class TreeSupport;
class Print; class Print;
class PrintObject; class PrintObject;
class SupportGeneratorLayer; struct SlicingParameters;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
namespace TreeSupport3D namespace TreeSupport3D
{ {
@ -90,7 +92,7 @@ struct SupportElementStateBits {
#endif // TREE_SUPPORTS_TRACK_LOST #endif // TREE_SUPPORTS_TRACK_LOST
deleted(false), deleted(false),
marked(false) marked(false)
{} {}
/*! /*!
* \brief The element trys to reach the buildplate * \brief The element trys to reach the buildplate
@ -108,7 +110,7 @@ struct SupportElementStateBits {
bool use_min_xy_dist : 1; bool use_min_xy_dist : 1;
/*! /*!
* \brief True if this Element or any parent provides support to a support roof. * \brief True if this Element or any parent (element above) provides support to a support roof.
*/ */
bool supports_roof : 1; bool supports_roof : 1;
@ -137,10 +139,6 @@ struct SupportElementStateBits {
struct SupportElementState : public SupportElementStateBits struct SupportElementState : public SupportElementStateBits
{ {
int type = 0;
coordf_t radius = 0;
float print_z = 0;
/*! /*!
* \brief The layer this support elements wants reach * \brief The layer this support elements wants reach
*/ */
@ -175,7 +173,7 @@ struct SupportElementState : public SupportElementStateBits
* \brief The resulting center point around which a circle will be drawn later. * \brief The resulting center point around which a circle will be drawn later.
* Will be set by setPointsOnAreas * Will be set by setPointsOnAreas
*/ */
Point result_on_layer{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; Point result_on_layer { std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; } bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; } void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
/*! /*!
@ -189,7 +187,7 @@ struct SupportElementState : public SupportElementStateBits
double elephant_foot_increases; double elephant_foot_increases;
/*! /*!
* \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move. * \brief The element tries to not move until this dtt is reached, is set to 0 if the element had to move.
*/ */
uint32_t dont_move_until; uint32_t dont_move_until;
@ -204,18 +202,19 @@ struct SupportElementState : public SupportElementStateBits
uint32_t missing_roof_layers; uint32_t missing_roof_layers;
// called by increase_single_area() and increaseAreas() // called by increase_single_area() and increaseAreas()
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState& src) [[nodiscard]] static SupportElementState propagate_down(const SupportElementState &src)
{ {
SupportElementState dst{ src }; SupportElementState dst{ src };
++dst.distance_to_top; ++ dst.distance_to_top;
--dst.layer_idx; -- dst.layer_idx;
// set to invalid as we are a new node on a new layer // set to invalid as we are a new node on a new layer
dst.result_on_layer_reset(); dst.result_on_layer_reset();
dst.skip_ovalisation = false; dst.skip_ovalisation = false;
return dst; return dst;
} }
};
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; }
};
/*! /*!
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch. * \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
@ -279,8 +278,6 @@ struct SupportElement
Polygons influence_area; Polygons influence_area;
}; };
void tree_supports_show_error(std::string_view message, bool critical);
using SupportElements = std::deque<SupportElement>; using SupportElements = std::deque<SupportElement>;
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem) [[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
@ -293,39 +290,27 @@ using SupportElements = std::deque<SupportElement>;
return support_element_collision_radius(settings, elem.state); return support_element_collision_radius(settings, elem.state);
} }
void create_layer_pathing(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
void create_nodes_from_area(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
void organic_smooth_branches_avoid_collisions(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<std::pair<SupportElement*, int>>& elements_with_link_down, const std::vector<size_t>& linear_data_layers, std::function<void()> throw_on_cancel);
indexed_triangle_set draw_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
void slice_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, const indexed_triangle_set& cummulative_mesh, SupportGeneratorLayersPtr& bottom_contacts, SupportGeneratorLayersPtr& top_contacts, SupportGeneratorLayersPtr& intermediate_layers, SupportGeneratorLayerStorage& layer_storage, std::function<void()> throw_on_cancel);
void generate_initial_areas(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, InterfacePlacer& interface_placer, std::function<void()> throw_on_cancel);
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced. // Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
void organic_draw_branches( void organic_draw_branches(
PrintObject& print_object, PrintObject &print_object,
TreeModelVolumes& volumes, TreeModelVolumes &volumes,
const TreeSupportSettings& config, const TreeSupportSettings &config,
std::vector<SupportElements>& move_bounds, std::vector<SupportElements> &move_bounds,
// I/O: // I/O:
SupportGeneratorLayersPtr& bottom_contacts, SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr& top_contacts, SupportGeneratorLayersPtr &top_contacts,
InterfacePlacer& interface_placer, InterfacePlacer &interface_placer,
// Output: // Output:
SupportGeneratorLayersPtr& intermediate_layers, SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage& layer_storage, SupportGeneratorLayerStorage &layer_storage,
std::function<void()> throw_on_cancel); std::function<void()> throw_on_cancel);
} // namespace TreeSupport3D } // namespace TreeSupport3D
void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_support, std::function<void()> throw_on_cancel = []{}); void generate_tree_support_3D(PrintObject &print_object, std::function<void()> throw_on_cancel = []{});
} // namespace Slic3r } // namespace Slic3r

View file

@ -1,212 +0,0 @@
// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine.
// Original source of Thomas Rahm's tree supports:
// https://github.com/ThomasRahm/CuraEngine
//
// Original CuraEngine copyright:
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#include "TreeSupportCommon.hpp"
namespace Slic3r::FFFTreeSupport {
TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object)
{
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &config = print_object.config();
const SlicingParameters &slicing_params = print_object.slicing_parameters();
// const std::vector<unsigned int> printing_extruders = print_object.object_extruders();
// Support must be enabled and set to Tree style.
assert(config.enable_support || config.enforce_support_layers > 0);
assert(is_tree(config.support_type));
// Calculate maximum external perimeter width over all printing regions, taking into account the default layer height.
coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = print_object.printing_region(region_id);
external_perimeter_width = std::max<coordf_t>(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width());
}
this->layer_height = scaled<coord_t>(config.layer_height.value);
this->resolution = scaled<coord_t>(print_config.resolution.value);
// Arache feature
this->min_feature_size = scaled<coord_t>(config.min_feature_size.value);
// +1 makes the threshold inclusive
this->support_angle = 0.5 * M_PI - std::clamp<double>((config.support_threshold_angle + 1) * M_PI / 180., 0., 0.5 * M_PI);
this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width();
this->support_roof_line_width = support_material_interface_flow(&print_object, config.layer_height).scaled_width();
//FIXME add it to SlicingParameters and reuse in both tree and normal supports?
this->support_bottom_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value != 0;
this->support_bottom_height = this->support_bottom_enable ?
(config.support_interface_bottom_layers.value > 0 ?
config.support_interface_bottom_layers.value :
config.support_interface_top_layers.value) * this->layer_height :
0;
this->support_material_buildplate_only = config.support_on_build_plate_only;
this->support_xy_distance = scaled<coord_t>(config.support_object_xy_distance.value);
// Separation of interfaces, it is likely smaller than support_xy_distance.
this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled<coord_t>(0.5 * external_perimeter_width));
this->support_top_distance = scaled<coord_t>(slicing_params.gap_support_object);
this->support_bottom_distance = scaled<coord_t>(slicing_params.gap_object_support);
// this->support_interface_skip_height =
// this->support_infill_angles =
this->support_roof_enable = config.support_interface_top_layers.value > 0;
this->support_roof_layers = this->support_roof_enable ? config.support_interface_top_layers.value : 0;
this->support_floor_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value > 0;
this->support_floor_layers = this->support_floor_enable ? config.support_interface_bottom_layers.value : 0;
// this->minimum_roof_area =
// this->support_roof_angles =
this->support_roof_pattern = config.support_interface_pattern;
this->support_pattern = config.support_base_pattern;
this->support_line_spacing = scaled<coord_t>(config.support_base_pattern_spacing.value);
// this->support_bottom_offset =
// this->support_wall_count = config.support_material_with_sheath ? 1 : 0;
this->support_wall_count = 1;
this->support_roof_line_distance = scaled<coord_t>(config.support_interface_spacing.value) + this->support_roof_line_width;
// this->minimum_support_area =
// this->minimum_bottom_area =
// this->support_offset =
this->support_tree_branch_distance = scaled<coord_t>(config.tree_support_branch_distance_organic.value);
this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle_organic * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_angle_slow = std::clamp<double>(config.tree_support_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter_organic.value);
this->support_tree_branch_diameter_angle = std::clamp<double>(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_top_rate = config.tree_support_top_rate.value; // percent
// this->support_tree_tip_diameter = this->support_line_width;
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.tree_support_tip_diameter.value), (coord_t)0, this->support_tree_branch_diameter);
std::cout << "\n---------------\n"
<< "layer_height: " << layer_height << "\nresolution: " << resolution << "\nmin_feature_size: " << min_feature_size
<< "\nsupport_angle: " << support_angle << "\nconfig.support_threshold_angle: " << config.support_threshold_angle << "\nsupport_line_width: " << support_line_width
<< "\nsupport_roof_line_width: " << support_roof_line_width << "\nsupport_bottom_enable: " << support_bottom_enable
<< "\nsupport_bottom_height: " << support_bottom_height
<< "\nsupport_material_buildplate_only: " << support_material_buildplate_only
<< "\nsupport_xy_distance: " << support_xy_distance << "\nsupport_xy_distance_overhang: " << support_xy_distance_overhang
<< "\nsupport_top_distance: " << support_top_distance << "\nsupport_bottom_distance: " << support_bottom_distance
<< "\nsupport_roof_enable: " << support_roof_enable << "\nsupport_roof_layers: " << support_roof_layers
<< "\nsupport_floor_enable: " << support_floor_enable << "\nsupport_floor_layers: " << support_floor_layers
<< "\nsupport_roof_pattern: " << support_roof_pattern << "\nsupport_pattern: " << support_pattern
<< "\nsupport_line_spacing: " << support_line_spacing << "\nsupport_wall_count: " << support_wall_count
<< "\nsupport_roof_line_distance: " << support_roof_line_distance
<< "\nsupport_tree_branch_distance: " << support_tree_branch_distance
<< "\nsupport_tree_angle_slow: " << support_tree_angle_slow
<< "\nsupport_tree_branch_diameter: " << support_tree_branch_diameter
<< "\nsupport_tree_branch_diameter_angle: " << support_tree_branch_diameter_angle
<< "\nsupport_tree_top_rate: " << support_tree_top_rate << "\nsupport_tree_tip_diameter: " << support_tree_tip_diameter
<< "\n---------------\n";
}
TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params)
: support_line_width(mesh_group_settings.support_line_width),
layer_height(mesh_group_settings.layer_height),
branch_radius(mesh_group_settings.support_tree_branch_diameter / 2),
min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance
maximum_move_distance((mesh_group_settings.support_tree_angle < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle) * layer_height) : std::numeric_limits<coord_t>::max()),
maximum_move_distance_slow((mesh_group_settings.support_tree_angle_slow < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
// Ensure lines always stack nicely even if layer height is large.
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)),
branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height),
max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2),
min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)),
increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2),
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / branch_radius_increase_per_layer),
support_rests_on_model(! mesh_group_settings.support_material_buildplate_only),
xy_distance(mesh_group_settings.support_xy_distance),
xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)),
bp_radius(mesh_group_settings.support_tree_bp_diameter / 2),
// Increase by half a line overlap, but not faster than 40 degrees angle (0 degrees means zero increase in radius).
bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))),
z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))),
// support_infill_angles(mesh_group_settings.support_infill_angles),
support_roof_angles(mesh_group_settings.support_roof_angles),
roof_pattern(mesh_group_settings.support_roof_pattern),
support_pattern(mesh_group_settings.support_pattern),
support_roof_line_width(mesh_group_settings.support_roof_line_width),
support_line_spacing(mesh_group_settings.support_line_spacing),
support_bottom_offset(mesh_group_settings.support_bottom_offset),
support_wall_count(mesh_group_settings.support_wall_count),
resolution(mesh_group_settings.resolution),
support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference.
settings(mesh_group_settings),
min_feature_size(mesh_group_settings.min_feature_size)
{
// At least one tip layer must be defined.
assert(tip_layers > 0);
layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer;
if (TreeSupportSettings::soluble) {
// safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely
// When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size
// This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance.
xy_min_distance = std::max(xy_min_distance, scaled<coord_t>(0.1));
xy_distance = std::max(xy_distance, xy_min_distance);
}
// const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } };
// interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority"));
//FIXME this was the default
// interface_preference = InterfacePreference::SupportLinesOverwriteInterface;
//interface_preference = InterfacePreference::SupportAreaOverwritesInterface;
interface_preference = InterfacePreference::InterfaceAreaOverwritesSupport;
if (slicing_params.raft_layers() > 0) {
// Fill in raft_layers with the heights of the layers below the first object layer.
// First layer
double z = slicing_params.first_print_layer_height;
this->raft_layers.emplace_back(z);
// Raft base layers
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
z += slicing_params.base_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft interface layers
for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) {
z += slicing_params.interface_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft contact layer
if (slicing_params.raft_layers() > 1) {
z = slicing_params.raft_contact_top_z;
this->raft_layers.emplace_back(z);
}
if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) {
// Layers between the raft contacts and bottom of the object.
auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height));
double step = dist_to_go / nsteps;
for (int i = 0; i < nsteps; ++ i) {
z += step;
this->raft_layers.emplace_back(z);
}
}
}
}
#if defined(TREE_SUPPORT_SHOW_ERRORS) && defined(_WIN32)
#define TREE_SUPPORT_SHOW_ERRORS_WIN32
#include <windows.h>
#endif
// Shared with generate_support_areas()
bool g_showed_critical_error = false;
bool g_showed_performance_warning = false;
void tree_supports_show_error(std::string_view message, bool critical)
{ // todo Remove! ONLY FOR PUBLIC BETA!!
printf("Error: %s, critical: %d\n", message.data(), int(critical));
#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
static bool showed_critical = false;
static bool showed_performance = false;
auto bugtype = std::string(critical ? " This is a critical bug. It may cause missing or malformed branches.\n" : "This bug should only decrease performance.\n");
bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning);
(critical ? g_showed_critical_error : g_showed_performance_warning) = true;
if (show)
MessageBoxA(nullptr, std::string("TreeSupport_2 MOD detected an error while generating the tree support.\nPlease report this back to me with profile and model.\nRevision 5.0\n" + std::string(message) + "\n" + bugtype).c_str(),
"Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
}
} // namespace Slic3r::FFFTreeSupport

View file

@ -15,12 +15,10 @@
#include <string_view> #include <string_view>
using namespace Slic3r::FFFSupport;
namespace Slic3r namespace Slic3r
{ {
namespace FFFTreeSupport namespace TreeSupport3D
{ {
using LayerIndex = int; using LayerIndex = int;
@ -36,7 +34,92 @@ enum class InterfacePreference
struct TreeSupportMeshGroupSettings { struct TreeSupportMeshGroupSettings {
TreeSupportMeshGroupSettings() = default; TreeSupportMeshGroupSettings() = default;
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object); explicit TreeSupportMeshGroupSettings(const PrintObject &print_object)
{
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &config = print_object.config();
const SlicingParameters &slicing_params = print_object.slicing_parameters();
// const std::vector<unsigned int> printing_extruders = print_object.object_extruders();
// Support must be enabled and set to Tree style.
assert(config.enable_support || config.enforce_support_layers > 0);
assert(is_tree(config.support_type));
// Calculate maximum external perimeter width over all printing regions, taking into account the default layer height.
coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = print_object.printing_region(region_id);
external_perimeter_width = std::max<coordf_t>(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width());
}
this->layer_height = scaled<coord_t>(config.layer_height.value);
this->resolution = scaled<coord_t>(print_config.resolution.value);
// Arache feature
this->min_feature_size = scaled<coord_t>(config.min_feature_size.value);
// +1 makes the threshold inclusive
this->support_angle = 0.5 * M_PI - std::clamp<double>((config.support_threshold_angle + 1) * M_PI / 180., 0., 0.5 * M_PI);
this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width();
this->support_roof_line_width = support_material_interface_flow(&print_object, config.layer_height).scaled_width();
//FIXME add it to SlicingParameters and reuse in both tree and normal supports?
this->support_bottom_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value != 0;
this->support_bottom_height = this->support_bottom_enable ?
(config.support_interface_bottom_layers.value > 0 ?
config.support_interface_bottom_layers.value :
config.support_interface_top_layers.value) * this->layer_height :
0;
this->support_material_buildplate_only = config.support_on_build_plate_only;
this->support_xy_distance = scaled<coord_t>(config.support_object_xy_distance.value);
// Separation of interfaces, it is likely smaller than support_xy_distance.
this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled<coord_t>(0.5 * external_perimeter_width));
this->support_top_distance = scaled<coord_t>(slicing_params.gap_support_object);
this->support_bottom_distance = scaled<coord_t>(slicing_params.gap_object_support);
// this->support_interface_skip_height =
// this->support_infill_angles =
this->support_roof_enable = config.support_interface_top_layers.value > 0;
this->support_roof_layers = this->support_roof_enable ? config.support_interface_top_layers.value : 0;
this->support_floor_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value > 0;
this->support_floor_layers = this->support_floor_enable ? config.support_interface_bottom_layers.value : 0;
// this->minimum_roof_area =
// this->support_roof_angles =
this->support_roof_pattern = config.support_interface_pattern;
this->support_pattern = config.support_base_pattern;
this->support_line_spacing = scaled<coord_t>(config.support_base_pattern_spacing.value);
// this->support_bottom_offset =
// this->support_wall_count = config.support_material_with_sheath ? 1 : 0;
this->support_wall_count = 1;
this->support_roof_line_distance = scaled<coord_t>(config.support_interface_spacing.value) + this->support_roof_line_width;
// this->minimum_support_area =
// this->minimum_bottom_area =
// this->support_offset =
this->support_tree_branch_distance = scaled<coord_t>(config.tree_support_branch_distance_organic.value);
this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle_organic * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_angle_slow = std::clamp<double>(config.tree_support_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter_organic.value);
this->support_tree_branch_diameter_angle = std::clamp<double>(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_top_rate = config.tree_support_top_rate.value; // percent
// this->support_tree_tip_diameter = this->support_line_width;
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.tree_support_tip_diameter.value), (coord_t)0, this->support_tree_branch_diameter);
std::cout << "\n---------------\n"
<< "layer_height: " << layer_height << "\nresolution: " << resolution << "\nmin_feature_size: " << min_feature_size
<< "\nsupport_angle: " << support_angle << "\nconfig.support_threshold_angle: " << config.support_threshold_angle << "\nsupport_line_width: " << support_line_width
<< "\nsupport_roof_line_width: " << support_roof_line_width << "\nsupport_bottom_enable: " << support_bottom_enable
<< "\nsupport_bottom_height: " << support_bottom_height
<< "\nsupport_material_buildplate_only: " << support_material_buildplate_only
<< "\nsupport_xy_distance: " << support_xy_distance << "\nsupport_xy_distance_overhang: " << support_xy_distance_overhang
<< "\nsupport_top_distance: " << support_top_distance << "\nsupport_bottom_distance: " << support_bottom_distance
<< "\nsupport_roof_enable: " << support_roof_enable << "\nsupport_roof_layers: " << support_roof_layers
<< "\nsupport_floor_enable: " << support_floor_enable << "\nsupport_floor_layers: " << support_floor_layers
<< "\nsupport_roof_pattern: " << support_roof_pattern << "\nsupport_pattern: " << support_pattern
<< "\nsupport_line_spacing: " << support_line_spacing << "\nsupport_wall_count: " << support_wall_count
<< "\nsupport_roof_line_distance: " << support_roof_line_distance
<< "\nsupport_tree_branch_distance: " << support_tree_branch_distance
<< "\nsupport_tree_angle_slow: " << support_tree_angle_slow
<< "\nsupport_tree_branch_diameter: " << support_tree_branch_diameter
<< "\nsupport_tree_branch_diameter_angle: " << support_tree_branch_diameter_angle
<< "\nsupport_tree_top_rate: " << support_tree_top_rate << "\nsupport_tree_tip_diameter: " << support_tree_tip_diameter
<< "\n---------------\n";
}
/*********************************************************************/ /*********************************************************************/
/* Print parameters, not support specific: */ /* Print parameters, not support specific: */
@ -209,7 +292,93 @@ struct TreeSupportSettings
{ {
public: public:
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class. TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class.
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params); explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params)
: support_line_width(mesh_group_settings.support_line_width),
layer_height(mesh_group_settings.layer_height),
branch_radius(mesh_group_settings.support_tree_branch_diameter / 2),
min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance
maximum_move_distance((mesh_group_settings.support_tree_angle < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle) * layer_height) : std::numeric_limits<coord_t>::max()),
maximum_move_distance_slow((mesh_group_settings.support_tree_angle_slow < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
// Ensure lines always stack nicely even if layer height is large.
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)),
branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height),
max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2),
min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)),
increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2),
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / branch_radius_increase_per_layer),
support_rests_on_model(! mesh_group_settings.support_material_buildplate_only),
xy_distance(mesh_group_settings.support_xy_distance),
xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)),
bp_radius(mesh_group_settings.support_tree_bp_diameter / 2),
// Increase by half a line overlap, but not faster than 40 degrees angle (0 degrees means zero increase in radius).
bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))),
z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))),
// support_infill_angles(mesh_group_settings.support_infill_angles),
support_roof_angles(mesh_group_settings.support_roof_angles),
roof_pattern(mesh_group_settings.support_roof_pattern),
support_pattern(mesh_group_settings.support_pattern),
support_roof_line_width(mesh_group_settings.support_roof_line_width),
support_line_spacing(mesh_group_settings.support_line_spacing),
support_bottom_offset(mesh_group_settings.support_bottom_offset),
support_wall_count(mesh_group_settings.support_wall_count),
resolution(mesh_group_settings.resolution),
support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference.
settings(mesh_group_settings),
min_feature_size(mesh_group_settings.min_feature_size)
{
// At least one tip layer must be defined.
assert(tip_layers > 0);
layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer;
if (TreeSupportSettings::soluble) {
// safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely
// When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size
// This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance.
xy_min_distance = std::max(xy_min_distance, scaled<coord_t>(0.1));
xy_distance = std::max(xy_distance, xy_min_distance);
}
// const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } };
// interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority"));
//FIXME this was the default
// interface_preference = InterfacePreference::SupportLinesOverwriteInterface;
//interface_preference = InterfacePreference::SupportAreaOverwritesInterface;
interface_preference = InterfacePreference::InterfaceAreaOverwritesSupport;
if (slicing_params.raft_layers() > 0) {
// Fill in raft_layers with the heights of the layers below the first object layer.
// First layer
double z = slicing_params.first_print_layer_height;
this->raft_layers.emplace_back(z);
// Raft base layers
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
z += slicing_params.base_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft interface layers
for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) {
z += slicing_params.interface_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft contact layer
if (slicing_params.raft_layers() > 1) {
z = slicing_params.raft_contact_top_z;
this->raft_layers.emplace_back(z);
}
if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) {
// Layers between the raft contacts and bottom of the object.
auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height));
double step = dist_to_go / nsteps;
for (int i = 0; i < nsteps; ++ i) {
z += step;
this->raft_layers.emplace_back(z);
}
}
}
}
// some static variables dependent on other meshes that are not currently processed. // some static variables dependent on other meshes that are not currently processed.
// Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy. // Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy.
@ -450,7 +619,20 @@ static constexpr const bool polygons_strictly_simple = false;
inline double tiny_area_threshold() { return sqr(scaled<double>(0.001)); } inline double tiny_area_threshold() { return sqr(scaled<double>(0.001)); }
void tree_supports_show_error(std::string_view message, bool critical); void tree_supports_show_error(std::string_view message, bool critical)
{ // todo Remove! ONLY FOR PUBLIC BETA!!
printf("Error: %s, critical: %d\n", message.data(), int(critical));
#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
static bool g_showed_critical_error = false;
static bool g_showed_performance_warning = false;
auto bugtype = std::string(critical ? " This is a critical bug. It may cause missing or malformed branches.\n" : "This bug should only decrease performance.\n");
bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning);
(critical ? g_showed_critical_error : g_showed_performance_warning) = true;
if (show)
MessageBoxA(nullptr, std::string("TreeSupport_2 MOD detected an error while generating the tree support.\nPlease report this back to me with profile and model.\nRevision 5.0\n" + std::string(message) + "\n" + bugtype).c_str(),
"Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
}
inline double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx) inline double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
{ {
@ -584,7 +766,7 @@ private:
std::mutex m_mutex_layer_storage; std::mutex m_mutex_layer_storage;
}; };
} // namespace FFFTreeSupport } // namespace TreeSupport3D
} // namespace Slic3r } // namespace Slic3r

File diff suppressed because it is too large Load diff

View file

@ -1,263 +0,0 @@
#ifndef slic3r_SupportMaterial_hpp_
#define slic3r_SupportMaterial_hpp_
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Slicing.hpp"
namespace Slic3r {
class PrintObject;
class PrintConfig;
class PrintObjectConfig;
// This class manages raft and supports for a single PrintObject.
// Instantiated by Slic3r::Print::Object->_support_material()
// This class is instantiated before the slicing starts as Object.pm will query
// the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial
{
public:
// Support layer type to be used by MyLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum SupporLayerType {
sltUnknown = 0,
// Ratft base layer, to be printed with the support material.
sltRaftBase,
// Raft interface layer, to be printed with the support interface material.
sltRaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
sltBottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an sltBottomContact layer.
sltBottomInterface,
// Sparse base support layer, to be printed with a support material.
sltBase,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with sltTopContact layer.
sltTopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
sltTopContact,
// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
sltIntermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the MyLayer is aware of the bridging flow and the interface gaps between the object and the support.
class MyLayer
{
public:
void reset() {
*this = MyLayer();
}
bool operator==(const MyLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const MyLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
} else if (print_z == layer2.print_z) {
if (height > layer2.height)
return true;
else if (height == layer2.height) {
// Bridging layers first.
return bridging && ! layer2.bridging;
} else
return false;
} else
return false;
}
void merge(MyLayer &&rhs) {
// The union_() does not support move semantic yet, but maybe one day it will.
this->polygons = union_(this->polygons, std::move(rhs.polygons));
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
if (! dst || dst->empty())
dst = std::move(src);
else if (src && ! src->empty())
*dst = union_(*dst, std::move(*src));
};
merge(this->contact_polygons, rhs.contact_polygons);
merge(this->overhang_polygons, rhs.overhang_polygons);
merge(this->enforcer_polygons, rhs.enforcer_polygons);
rhs.reset();
}
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
coordf_t bottom_print_z() const { return print_z - height; }
// To sort the extremes of top / bottom interface layers.
coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; }
SupporLayerType layer_type { sltUnknown };
// Z used for printing, in unscaled coordinates.
coordf_t print_z { 0 };
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z { 0 };
// Layer height in unscaled coordinates.
coordf_t height { 0 };
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_above { size_t(-1) };
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_below { size_t(-1) };
// Use a bridging flow when printing this support layer.
bool bridging { false };
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only.
std::unique_ptr<Polygons> contact_polygons;
std::unique_ptr<Polygons> overhang_polygons;
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
std::unique_ptr<Polygons> enforcer_polygons;
};
struct SupportParams {
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t support_expansion;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
};
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
typedef std::deque<MyLayer> MyLayerStorage;
typedef std::vector<MyLayer*> MyLayersPtr;
public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
// Is raft enabled?
bool has_raft() const { return m_slicing_params.has_raft(); }
// Has any support?
bool has_support() const { return m_object_config->enable_support.value || m_object_config->enforce_support_layers; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_on_build_plate_only.value; }
// BBS
bool synchronize_layers() const { return /*m_slicing_params.soluble_interface && */!m_print_config->independent_support_layer_height.value; }
bool has_contact_loops() const { return m_object_config->support_interface_loop_pattern.value; }
// Generate support material for the object.
// New support layers will be added to the object,
// with extrusion paths and islands filled in for each support layer.
void generate(PrintObject &object);
private:
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
MyLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, MyLayerStorage &layer_storage) const;
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
MyLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const MyLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
MyLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
MyLayersPtr raft_and_intermediate_support_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayerStorage &layer_storage) const;
// Fill in the base layers with polygons.
void generate_base_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const;
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
MyLayersPtr generate_raft_base(
const PrintObject &object,
const MyLayersPtr &top_contacts,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers,
const MyLayersPtr &base_layers,
MyLayerStorage &layer_storage) const;
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
std::pair<MyLayersPtr, MyLayersPtr> generate_interface_layers(
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
MyLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between
// the support volume and the object.
void trim_support_layers_by_object(
const PrintObject &object,
MyLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
/*
void generate_pillars_shape();
void clip_with_shape();
*/
// Produce the actual G-code.
void generate_toolpaths(
SupportLayerPtrs &support_layers,
const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
const MyLayersPtr &intermediate_layers,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers) const;
// Following objects are not owned by SupportMaterial class.
const PrintObject *m_object;
const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParams m_support_params;
};
} // namespace Slic3r
#endif /* slic3r_SupportMaterial_hpp_ */

File diff suppressed because it is too large Load diff

View file

@ -1,511 +0,0 @@
#ifndef TREESUPPORT_H
#define TREESUPPORT_H
#include <forward_list>
#include <unordered_set>
#include "ExPolygon.hpp"
#include "Point.hpp"
#include "Slicing.hpp"
#include "MinimumSpanningTree.hpp"
#include "tbb/concurrent_unordered_map.h"
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Fill/Lightning/Generator.hpp"
#ifndef SQ
#define SQ(x) ((x)*(x))
#endif
namespace Slic3r
{
class PrintObject;
class TreeSupport;
class SupportLayer;
struct LayerHeightData
{
coordf_t print_z = 0;
coordf_t height = 0;
size_t next_layer_nr = 0;
LayerHeightData() = default;
LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {}
};
struct TreeNode {
Vec3f pos;
std::vector<int> children; // index of children in the storing vector
std::vector<int> parents; // index of parents in the storing vector
TreeNode(Point pt, float z) {
pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z };
}
};
/*!
* \brief Lazily generates tree guidance volumes.
*
* \warning This class is not currently thread-safe and should not be accessed in OpenMP blocks
*/
class TreeSupportData
{
public:
TreeSupportData() = default;
/*!
* \brief Construct the TreeSupportData object
*
* \param xy_distance The required clearance between the model and the
* tree branches.
* \param max_move The maximum allowable movement between nodes on
* adjacent layers
* \param radius_sample_resolution Sample size used to round requested node radii.
* \param collision_resolution
*/
TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution);
TreeSupportData(TreeSupportData&&) = default;
TreeSupportData& operator=(TreeSupportData&&) = default;
TreeSupportData(const TreeSupportData&) = delete;
TreeSupportData& operator=(const TreeSupportData&) = delete;
/*!
* \brief Creates the areas that have to be avoided by the tree's branches.
*
* The result is a 2D area that would cause nodes of radius \p radius to
* collide with the model.
*
* \param radius The radius of the node of interest
* \param layer The layer of interest
* \return Polygons object
*/
const ExPolygons& get_collision(coordf_t radius, size_t layer_idx) const;
/*!
* \brief Creates the areas that have to be avoided by the tree's branches
* in order to reach the build plate.
*
* The result is a 2D area that would cause nodes of radius \p radius to
* collide with the model or be unable to reach the build platform.
*
* The input collision areas are inset by the maximum move distance and
* propagated upwards.
*
* \param radius The radius of the node of interest
* \param layer The layer of interest
* \return Polygons object
*/
const ExPolygons& get_avoidance(coordf_t radius, size_t layer_idx, int recursions=0) const;
Polygons get_contours(size_t layer_nr) const;
Polygons get_contours_with_holes(size_t layer_nr) const;
std::vector<LayerHeightData> layer_heights;
std::vector<TreeNode> tree_nodes;
private:
/*!
* \brief Convenience typedef for the keys to the caches
*/
struct RadiusLayerPair {
coordf_t radius;
size_t layer_nr;
int recursions;
};
struct RadiusLayerPairEquality {
constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const {
return _Left.radius == _Right.radius && _Left.layer_nr == _Right.layer_nr;
}
};
struct RadiusLayerPairHash {
size_t operator()(const RadiusLayerPair& elem) const {
return std::hash<coord_t>()(elem.radius) ^ std::hash<coord_t>()(elem.layer_nr * 7919);
}
};
/*!
* \brief Round \p radius upwards to a multiple of m_radius_sample_resolution
*
* \param radius The radius of the node of interest
*/
coordf_t ceil_radius(coordf_t radius) const;
/*!
* \brief Calculate the collision areas at the radius and layer indicated
* by \p key.
*
* \param key The radius and layer of the node of interest
*/
const ExPolygons& calculate_collision(const RadiusLayerPair& key) const;
/*!
* \brief Calculate the avoidance areas at the radius and layer indicated
* by \p key.
*
* \param key The radius and layer of the node of interest
*/
const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const;
public:
bool is_slim = false;
/*!
* \brief The required clearance between the model and the tree branches
*/
coordf_t m_xy_distance;
/*!
* \brief The maximum distance that the centrepoint of a tree branch may
* move in consequtive layers
*/
coordf_t m_max_move;
/*!
* \brief Sample resolution for radius values.
*
* The radius will be rounded (upwards) to multiples of this value before
* calculations are done when collision, avoidance and internal model
* Polygons are requested.
*/
coordf_t m_radius_sample_resolution;
/*!
* \brief Storage for layer outlines of the meshes.
*/
std::vector<ExPolygons> m_layer_outlines;
// union contours of all layers below
std::vector<ExPolygons> m_layer_outlines_below;
/*!
* \brief Caches for the collision, avoidance and internal model polygons
* at given radius and layer indices.
*
* These are mutable to allow modification from const function. This is
* generally considered OK as the functions are still logically const
* (ie there is no difference in behaviour for the user betweeen
* calculating the values each time vs caching the results).
*
* coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for.
* So we change to tbb::concurrent_unordered_map
*/
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_collision_cache;
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_avoidance_cache;
friend TreeSupport;
};
struct LineHash {
size_t operator()(const Line& line) const {
return (std::hash<coord_t>()(line.a(0)) ^ std::hash<coord_t>()(line.b(1))) * 102 +
(std::hash<coord_t>()(line.a(1)) ^ std::hash<coord_t>()(line.b(0))) * 10222;
}
};
/*!
* \brief Generates a tree structure to support your models.
*/
class TreeSupport
{
public:
/*!
* \brief Creates an instance of the tree support generator.
*
* \param storage The data storage to get global settings from.
*/
TreeSupport(PrintObject& object, const SlicingParameters &slicing_params);
/*!
* \brief Create the areas that need support.
*
* These areas are stored inside the given SliceDataStorage object.
* \param storage The data storage where the mesh data is gotten from and
* where the resulting support areas are stored.
*/
void generate();
void detect_overhangs(bool detect_first_sharp_tail_only=false);
enum NodeType {
eCircle,
eSquare,
ePolygon
};
/*!
* \brief Represents the metadata of a node in the tree.
*/
struct Node
{
static constexpr Node* NO_PARENT = nullptr;
Node()
: distance_to_top(0)
, position(Point(0, 0))
, obj_layer_nr(0)
, support_roof_layers_below(0)
, support_floor_layers_above(0)
, to_buildplate(true)
, parent(nullptr)
, print_z(0.0)
, height(0.0)
{}
// when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height;
Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0)
: distance_to_top(distance_to_top)
, position(position)
, obj_layer_nr(obj_layer_nr)
, support_roof_layers_below(support_roof_layers_below)
, support_floor_layers_above(0)
, to_buildplate(to_buildplate)
, parent(parent)
, print_z(print_z_)
, height(height_)
, dist_mm_to_top(dist_mm_to_top_)
{
if (parent) {
type = parent->type;
overhang = parent->overhang;
if (dist_mm_to_top==0)
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
parent->child = this;
for (auto& neighbor : parent->merged_neighbours)
neighbor->child = this;
}
}
#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node.
~Node()
{
parent = nullptr;
merged_neighbours.clear();
}
#endif // DEBUG
/*!
* \brief The number of layers to go to the top of this branch.
* Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded.
*/
int distance_to_top;
coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm
/*!
* \brief The position of this node on the layer.
*/
Point position;
Point movement; // movement towards neighbor center or outline
mutable double radius = 0.0;
mutable double max_move_dist = 0.0;
NodeType type = eCircle;
bool is_merged = false; // this node is generated by merging upper nodes
bool is_corner = false;
bool is_processed = false;
const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area
/*!
* \brief The direction of the skin lines above the tip of the branch.
*
* This determines in which direction we should reduce the width of the
* branch.
*/
bool skin_direction;
/*!
* \brief The number of support roof layers below this one.
*
* When a contact point is created, it is determined whether the mesh
* needs to be supported with support roof or not, since that is a
* per-mesh setting. This is stored in this variable in order to track
* how far we need to extend that support roof downwards.
*/
int support_roof_layers_below;
int support_floor_layers_above;
int obj_layer_nr;
/*!
* \brief Whether to try to go towards the build plate.
*
* If the node is inside the collision areas, it has no choice but to go
* towards the model. If it is not inside the collision areas, it must
* go towards the build plate to prevent a scar on the surface.
*/
bool to_buildplate;
/*!
* \brief The originating node for this one, one layer higher.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
Node *parent;
Node *child = nullptr;
/*!
* \brief All neighbours (on the same layer) that where merged into this node.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
std::list<Node*> merged_neighbours;
coordf_t print_z;
coordf_t height;
bool operator==(const Node& other) const
{
return position == other.position;
}
};
struct SupportParams
{
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
coordf_t support_extrusion_width;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
const double thresh_big_overhang = SQ(scale_(10));
};
int avg_node_per_layer = 0;
float nodes_angle = 0;
bool has_overhangs = false;
bool has_sharp_tails = false;
bool has_cantilever = false;
double max_cantilever_dist = 0;
SupportType support_type;
SupportMaterialStyle support_style;
std::unique_ptr<FillLightning::Generator> generator;
std::unordered_map<double, size_t> printZ_to_lightninglayer;
private:
/*!
* \brief Generator for model collision, avoidance and internal guide volumes
*
* Lazily computes volumes as needed.
* \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks
*/
std::shared_ptr<TreeSupportData> m_ts_data;
PrintObject *m_object;
const PrintObjectConfig *m_object_config;
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParams m_support_params;
size_t m_raft_layers = 0;
size_t m_highest_overhang_layer = 0;
std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees;
std::vector< std::unordered_map<Line, bool, LineHash>> m_mst_line_x_layer_contour_caches;
coordf_t MAX_BRANCH_RADIUS = 10.0;
coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
coordf_t MIN_BRANCH_RADIUS = 0.5;
float tree_support_branch_diameter_angle = 5.0;
bool is_strong = false;
bool is_slim = false;
bool with_infill = false;
/*!
* \brief Polygons representing the limits of the printable area of the
* machine
*/
ExPolygon m_machine_border;
/*!
* \brief Draws circles around each node of the tree into the final support.
*
* This also handles the areas that have to become support roof, support
* bottom, the Z distances, etc.
*
* \param storage[in, out] The settings storage to get settings from and to
* save the resulting support polygons to.
* \param contact_nodes The nodes to draw as support.
*/
void draw_circles(const std::vector<std::vector<Node*>>& contact_nodes);
/*!
* \brief Drops down the nodes of the tree support towards the build plate.
*
* This is where the cleverness of tree support comes in: The nodes stay on
* their 2D layers but on the next layer they are slightly shifted. This
* causes them to move towards each other as they are copied to lower layers
* which ultimately results in a 3D tree.
*
* \param contact_nodes[in, out] The nodes in the space that need to be
* dropped down. The nodes are dropped to lower layers inside the same
* vector of layers.
*/
void drop_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes);
void adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes);
/*! BBS: MusangKing: maximum layer height
* \brief Optimize the generation of tree support by pre-planning the layer_heights
*
*/
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes);
/*!
* \brief Creates points where support contacts the model.
*
* A set of points is created for each layer.
* \param mesh The mesh to get the overhang areas to support of.
* \param contact_nodes[out] A vector of mappings from contact points to
* their tree nodes.
* \param collision_areas For every layer, the areas where a generated
* contact point would immediately collide with the model due to the X/Y
* distance.
* \return For each layer, a list of points where the tree should connect
* with the model.
*/
void generate_contact_points(std::vector<std::vector<Node*>>& contact_nodes);
/*!
* \brief Add a node to the next layer.
*
* If a node is already at that position in the layer, the nodes are merged.
*/
void insert_dropped_node(std::vector<Node*>& nodes_layer, Node* node);
void create_tree_support_layers();
void generate_toolpaths();
Polygons spanning_tree_to_polygon(const std::vector<MinimumSpanningTree>& spanning_trees, Polygons layer_contours, int layer_nr);
Polygons contact_nodes_to_polygon(const std::vector<Node*>& contact_nodes, Polygons layer_contours, int layer_nr, std::vector<double>& radiis, std::vector<bool>& is_interface);
coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor);
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor);
// similar to SupportMaterial::trim_support_layers_by_object
Polygons get_trim_support_regions(
const PrintObject& object,
SupportLayer* support_layer_ptr,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy);
};
}
#endif /* TREESUPPORT_H */