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
Support/SupportCommon.cpp
Support/SupportCommon.hpp
Support/SupportDebug.cpp
Support/SupportDebug.hpp
Support/SupportLayer.hpp
# Support/SupportMaterial.cpp
# Support/SupportMaterial.hpp
Support/SupportParameters.cpp
Support/SupportMaterial.cpp
Support/SupportMaterial.hpp
Support/SupportParameters.hpp
Support/OrganicSupport.cpp
Support/OrganicSupport.hpp
Support/TreeSupport.cpp
Support/SupportSpotsGenerator.cpp
Support/SupportSpotsGenerator.hpp
Support/TreeSupport.hpp
Support/TreeSupportCommon.cpp
Support/TreeSupport.cpp
Support/TreeSupport3D.cpp
Support/TreeSupport3D.hpp
Support/TreeSupportCommon.hpp
Support/TreeModelVolumes.cpp
Support/TreeModelVolumes.hpp
SupportMaterial.cpp
SupportMaterial.hpp
PrincipalComponents2D.cpp
PrincipalComponents2D.hpp
SupportSpotsGenerator.cpp
SupportSpotsGenerator.hpp
TreeSupport.hpp
TreeSupport.cpp
MinimumSpanningTree.hpp
MinimumSpanningTree.cpp
Surface.cpp

View file

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

View file

@ -8,9 +8,10 @@
#include "Layer.hpp"
#include "MutablePolygon.hpp"
#include "PrintConfig.hpp"
#include "SupportMaterial.hpp"
#include "SupportSpotsGenerator.hpp"
#include "Support/SupportMaterial.hpp"
#include "Support/SupportSpotsGenerator.hpp"
#include "Support/TreeSupport.hpp"
#include "Support/TreeSupport3D.hpp"
#include "Surface.hpp"
#include "Slicing.hpp"
#include "Tesselate.hpp"
@ -19,7 +20,6 @@
#include "Fill/FillAdaptive.hpp"
#include "Fill/FillLightning.hpp"
#include "Format/STL.hpp"
#include "TreeSupport.hpp"
#include "format.hpp"
#include <float.h>
@ -3528,7 +3528,7 @@ void PrintObject::_generate_support_material()
if (this->config().support_style.value == smsOrganic ||
// Orca: use organic as default
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 {
TreeSupport tree_support(*this, m_slicing_params);
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>
namespace Slic3r::FFFSupport {
namespace Slic3r {
// how much we extend support around the actual contact area
//FIXME this should be dependent on the nozzle diameter!

View file

@ -12,8 +12,6 @@ namespace Slic3r {
class PrintObject;
class SupportLayer;
namespace FFFSupport {
// Remove bridges from support contact areas.
// To be called if PrintObjectConfig::dont_support_bridges.
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);
}
} // namespace FFFSupport
} // namespace Slic3r
#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 "../Polygon.hpp"
namespace Slic3r::FFFSupport {
namespace Slic3r {
// 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.

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,15 @@
#ifndef slic3r_SupportMaterial_hpp_
#define slic3r_SupportMaterial_hpp_
#include "../Flow.hpp"
#include "../PrintConfig.hpp"
#include "../Slicing.hpp"
#include "SupportLayer.hpp"
#include "SupportParameters.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()
@ -18,6 +17,142 @@ class PrintObject;
// 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);
@ -26,8 +161,8 @@ public:
// 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; }
bool synchronize_layers() const { return m_slicing_params.soluble_interface && m_print_config->independent_support_layer_height.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.
@ -36,47 +171,63 @@ public:
void generate(PrintObject &object);
private:
using SupportGeneratorLayersPtr = FFFSupport::SupportGeneratorLayersPtr;
using SupportGeneratorLayerStorage = FFFSupport::SupportGeneratorLayerStorage;
using SupportParameters = FFFSupport::SupportParameters;
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.
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.
// 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.
SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
SupportGeneratorLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
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 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.
SupportGeneratorLayersPtr raft_and_intermediate_support_layers(
MyLayersPtr raft_and_intermediate_support_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayerStorage &layer_storage) const;
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 SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
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,
SupportGeneratorLayersPtr &support_layers,
MyLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
@ -86,14 +237,25 @@ private:
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.
SupportParameters m_support_params;
SupportParams m_support_params;
};
} // 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;
enum InfillPattern : int;
namespace FFFSupport {
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.
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.)); }
};
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportParameters_hpp_ */

View file

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

View file

@ -26,7 +26,7 @@ namespace Slic3r
class BuildVolume;
class PrintObject;
namespace FFFTreeSupport
namespace TreeSupport3D
{
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
@ -548,7 +548,7 @@ private:
#endif // SLIC3R_TREESUPPORTS_PROGRESS
};
} // namespace FFFTreeSupport
} // namespace TreeSupport3D
} // namespace Slic3r
#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.
// 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.
#ifndef TREESUPPORT_H
#define TREESUPPORT_H
#ifndef slic3r_TreeSupport_hpp
#define slic3r_TreeSupport_hpp
#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"
#include "SupportLayer.hpp"
#include "TreeModelVolumes.hpp"
#include "TreeSupportCommon.hpp"
#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
#ifndef SQ
#define SQ(x) ((x)*(x))
#endif
namespace Slic3r
{
// Forward declarations
class Print;
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.
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
struct AreaIncreaseSettings
{
AreaIncreaseSettings(
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;
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 };
}
};
#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.
struct SupportElementStateBits {
SupportElementStateBits() :
to_buildplate(false),
to_model_gracious(false),
use_min_xy_dist(false),
supports_roof(false),
can_use_safe_radius(false),
skip_ovalisation(false),
#ifdef TREE_SUPPORTS_TRACK_LOST
lost(false),
verylost(false),
#endif // TREE_SUPPORTS_TRACK_LOST
deleted(false),
marked(false)
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)
{}
/*!
* \brief The element trys to reach the buildplate
*/
bool to_buildplate : 1;
// 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 Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
* \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 to_model_gracious : 1;
int distance_to_top;
coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm
/*!
* \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 position of this node on the layer.
*/
bool use_min_xy_dist : 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 True if this Element or any parent (element above) provides support to a support roof.
* \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 supports_roof : 1;
bool skin_direction;
/*!
* \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 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.
*/
bool can_use_safe_radius : 1;
int support_roof_layers_below;
int support_floor_layers_above;
int obj_layer_nr;
/*!
* \brief Skip the ovalisation to parent and children when generating the final circles.
* \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 skip_ovalisation : 1;
bool to_buildplate;
#ifdef TREE_SUPPORTS_TRACK_LOST
// Likely a lost branch, debugging information.
bool lost : 1;
bool verylost : 1;
#endif // TREE_SUPPORTS_TRACK_LOST
/*!
* \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;
// Not valid anymore, to be deleted.
bool deleted : 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;
// General purpose flag marking a visited element.
bool marked : 1;
coordf_t print_z;
coordf_t height;
bool operator==(const Node& other) const
{
return position == other.position;
}
};
struct SupportElementState : public SupportElementStateBits
struct SupportParams
{
/*!
* \brief The layer this support elements wants reach
*/
LayerIndex target_height;
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;
/*!
* \brief The position this support elements wants to support on layer=target_height
*/
Point target_position;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
/*!
* \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;
coordf_t gap_xy;
/*!
* \brief The next height this support elements wants to reach
*/
LayerIndex layer_idx;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
/*!
* \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 };
++ dst.distance_to_top;
-- dst.layer_idx;
// set to invalid as we are a new node on a new layer
dst.result_on_layer_reset();
dst.skip_ovalisation = false;
return dst;
}
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; }
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 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.
* \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
*/
[[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;
}
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 Get the Radius, that this element will have.
* \param elem[in] The Element.
* \return The radius the element has.
* \brief Polygons representing the limits of the printable area of the
* machine
*/
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return settings.getRadius(getEffectiveDTT(settings, elem), elem.elephant_foot_increases);
}
ExPolygon m_machine_border;
/*!
* \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.
* \param elem[in] The Element.
* \return The collision radius the element has.
* \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.
*/
[[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
{
using ParentIndices =
#ifdef NDEBUG
// To reduce memory allocation in release mode.
boost::container::small_vector<int32_t, 4>;
#else // NDEBUG
// To ease debugging.
std::vector<int32_t>;
#endif // NDEBUG
// SupportElement(const SupportElementState &state) : SupportElementState(state) {}
SupportElement(const SupportElementState &state, Polygons &&influence_area) : state(state), influence_area(std::move(influence_area)) {}
SupportElement(const SupportElementState &state, ParentIndices &&parents, Polygons &&influence_area) :
state(state), parents(std::move(parents)), influence_area(std::move(influence_area)) {}
SupportElementState state;
void draw_circles(const std::vector<std::vector<Node*>>& contact_nodes);
/*!
* \brief All elements in the layer above the current one that are supported by this element
* \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.
*/
ParentIndices parents;
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 The resulting influence area.
* Will only be set in the results of createLayerPathing, and will be nullptr inside!
* \brief Add a node to the next layer.
*
* If a node is already at that position in the layer, the nodes are merged.
*/
Polygons influence_area;
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)
{
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 */
#endif /* TREESUPPORT_H */

File diff suppressed because it is too large Load diff

View file

@ -9,13 +9,17 @@
#ifndef slic3r_TreeSupport_hpp
#define slic3r_TreeSupport_hpp
#include <boost/container/small_vector.hpp>
#include "../Point.hpp"
#include "../BoundingBox.hpp"
#include "../Utils.hpp"
#include "SupportLayer.hpp"
#include "TreeModelVolumes.hpp"
#include "TreeSupportCommon.hpp"
#include "../BoundingBox.hpp"
#include "../Point.hpp"
#include "../Utils.hpp"
#include <boost/container/small_vector.hpp>
// #define TREE_SUPPORT_SHOW_ERRORS
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
@ -36,11 +40,9 @@ namespace Slic3r
{
// Forward declarations
class TreeSupport;
class Print;
class PrintObject;
class SupportGeneratorLayer;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
struct SlicingParameters;
namespace TreeSupport3D
{
@ -108,7 +110,7 @@ struct SupportElementStateBits {
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;
@ -137,10 +139,6 @@ struct SupportElementStateBits {
struct SupportElementState : public SupportElementStateBits
{
int type = 0;
coordf_t radius = 0;
float print_z = 0;
/*!
* \brief The layer this support elements wants reach
*/
@ -189,7 +187,7 @@ struct SupportElementState : public SupportElementStateBits
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;
@ -214,8 +212,9 @@ struct SupportElementState : public SupportElementStateBits
dst.skip_ovalisation = false;
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.
@ -279,8 +278,6 @@ struct SupportElement
Polygons influence_area;
};
void tree_supports_show_error(std::string_view message, bool critical);
using SupportElements = std::deque<SupportElement>;
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
@ -293,18 +290,6 @@ using SupportElements = std::deque<SupportElement>;
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.
void organic_draw_branches(
PrintObject &print_object,
@ -325,7 +310,7 @@ void organic_draw_branches(
} // 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

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>
using namespace Slic3r::FFFSupport;
namespace Slic3r
{
namespace FFFTreeSupport
namespace TreeSupport3D
{
using LayerIndex = int;
@ -36,7 +34,92 @@ enum class InterfacePreference
struct TreeSupportMeshGroupSettings {
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: */
@ -209,7 +292,93 @@ struct TreeSupportSettings
{
public:
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.
// 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)); }
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)
{
@ -584,7 +766,7 @@ private:
std::mutex m_mutex_layer_storage;
};
} // namespace FFFTreeSupport
} // namespace TreeSupport3D
} // 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 */