mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-08 23:46:24 -06:00
Update files related to support to match BambuStudio's project structure
This commit is contained in:
parent
1b367b7df9
commit
97d1745e5a
28 changed files with 7619 additions and 16152 deletions
|
@ -307,29 +307,21 @@ set(lisbslic3r_sources
|
||||||
SlicingAdaptive.hpp
|
SlicingAdaptive.hpp
|
||||||
Support/SupportCommon.cpp
|
Support/SupportCommon.cpp
|
||||||
Support/SupportCommon.hpp
|
Support/SupportCommon.hpp
|
||||||
Support/SupportDebug.cpp
|
|
||||||
Support/SupportDebug.hpp
|
|
||||||
Support/SupportLayer.hpp
|
Support/SupportLayer.hpp
|
||||||
# Support/SupportMaterial.cpp
|
Support/SupportMaterial.cpp
|
||||||
# Support/SupportMaterial.hpp
|
Support/SupportMaterial.hpp
|
||||||
Support/SupportParameters.cpp
|
|
||||||
Support/SupportParameters.hpp
|
Support/SupportParameters.hpp
|
||||||
Support/OrganicSupport.cpp
|
Support/SupportSpotsGenerator.cpp
|
||||||
Support/OrganicSupport.hpp
|
Support/SupportSpotsGenerator.hpp
|
||||||
Support/TreeSupport.cpp
|
|
||||||
Support/TreeSupport.hpp
|
Support/TreeSupport.hpp
|
||||||
Support/TreeSupportCommon.cpp
|
Support/TreeSupport.cpp
|
||||||
|
Support/TreeSupport3D.cpp
|
||||||
|
Support/TreeSupport3D.hpp
|
||||||
Support/TreeSupportCommon.hpp
|
Support/TreeSupportCommon.hpp
|
||||||
Support/TreeModelVolumes.cpp
|
Support/TreeModelVolumes.cpp
|
||||||
Support/TreeModelVolumes.hpp
|
Support/TreeModelVolumes.hpp
|
||||||
SupportMaterial.cpp
|
|
||||||
SupportMaterial.hpp
|
|
||||||
PrincipalComponents2D.cpp
|
PrincipalComponents2D.cpp
|
||||||
PrincipalComponents2D.hpp
|
PrincipalComponents2D.hpp
|
||||||
SupportSpotsGenerator.cpp
|
|
||||||
SupportSpotsGenerator.hpp
|
|
||||||
TreeSupport.hpp
|
|
||||||
TreeSupport.cpp
|
|
||||||
MinimumSpanningTree.hpp
|
MinimumSpanningTree.hpp
|
||||||
MinimumSpanningTree.cpp
|
MinimumSpanningTree.cpp
|
||||||
Surface.cpp
|
Surface.cpp
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "Geometry/ConvexHull.hpp"
|
#include "Geometry/ConvexHull.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "ShortestPath.hpp"
|
#include "ShortestPath.hpp"
|
||||||
#include "Support/SupportMaterial.hpp"
|
|
||||||
#include "Thread.hpp"
|
#include "Thread.hpp"
|
||||||
#include "Time.hpp"
|
#include "Time.hpp"
|
||||||
#include "GCode.hpp"
|
#include "GCode.hpp"
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
#include "Layer.hpp"
|
#include "Layer.hpp"
|
||||||
#include "MutablePolygon.hpp"
|
#include "MutablePolygon.hpp"
|
||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
#include "SupportMaterial.hpp"
|
#include "Support/SupportMaterial.hpp"
|
||||||
#include "SupportSpotsGenerator.hpp"
|
#include "Support/SupportSpotsGenerator.hpp"
|
||||||
#include "Support/TreeSupport.hpp"
|
#include "Support/TreeSupport.hpp"
|
||||||
|
#include "Support/TreeSupport3D.hpp"
|
||||||
#include "Surface.hpp"
|
#include "Surface.hpp"
|
||||||
#include "Slicing.hpp"
|
#include "Slicing.hpp"
|
||||||
#include "Tesselate.hpp"
|
#include "Tesselate.hpp"
|
||||||
|
@ -19,7 +20,6 @@
|
||||||
#include "Fill/FillAdaptive.hpp"
|
#include "Fill/FillAdaptive.hpp"
|
||||||
#include "Fill/FillLightning.hpp"
|
#include "Fill/FillLightning.hpp"
|
||||||
#include "Format/STL.hpp"
|
#include "Format/STL.hpp"
|
||||||
#include "TreeSupport.hpp"
|
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
@ -3528,7 +3528,7 @@ void PrintObject::_generate_support_material()
|
||||||
if (this->config().support_style.value == smsOrganic ||
|
if (this->config().support_style.value == smsOrganic ||
|
||||||
// Orca: use organic as default
|
// Orca: use organic as default
|
||||||
this->config().support_style.value == smsDefault) {
|
this->config().support_style.value == smsDefault) {
|
||||||
fff_tree_support_generate(*this, std::function<void()>([this]() { this->throw_if_canceled(); }));
|
generate_tree_support_3D(*this, std::function<void()>([this]() { this->throw_if_canceled(); }));
|
||||||
} else {
|
} else {
|
||||||
TreeSupport tree_support(*this, m_slicing_params);
|
TreeSupport tree_support(*this, m_slicing_params);
|
||||||
tree_support.generate();
|
tree_support.generate();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace Slic3r::FFFSupport {
|
namespace Slic3r {
|
||||||
|
|
||||||
// how much we extend support around the actual contact area
|
// how much we extend support around the actual contact area
|
||||||
//FIXME this should be dependent on the nozzle diameter!
|
//FIXME this should be dependent on the nozzle diameter!
|
||||||
|
|
|
@ -12,8 +12,6 @@ namespace Slic3r {
|
||||||
class PrintObject;
|
class PrintObject;
|
||||||
class SupportLayer;
|
class SupportLayer;
|
||||||
|
|
||||||
namespace FFFSupport {
|
|
||||||
|
|
||||||
// Remove bridges from support contact areas.
|
// Remove bridges from support contact areas.
|
||||||
// To be called if PrintObjectConfig::dont_support_bridges.
|
// To be called if PrintObjectConfig::dont_support_bridges.
|
||||||
void remove_bridges_from_contacts(
|
void remove_bridges_from_contacts(
|
||||||
|
@ -150,8 +148,6 @@ int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL fn_lo
|
||||||
return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal);
|
return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FFFSupport
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif /* slic3r_SupportCommon_hpp_ */
|
#endif /* slic3r_SupportCommon_hpp_ */
|
||||||
|
|
|
@ -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 */
|
|
|
@ -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_ */
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "../ClipperUtils.hpp"
|
#include "../ClipperUtils.hpp"
|
||||||
#include "../Polygon.hpp"
|
#include "../Polygon.hpp"
|
||||||
|
|
||||||
namespace Slic3r::FFFSupport {
|
namespace Slic3r {
|
||||||
|
|
||||||
// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information
|
// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information
|
||||||
// about the support layer type than the final support layers stored in a PrintObject.
|
// about the support layer type than the final support layers stored in a PrintObject.
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,16 +1,15 @@
|
||||||
#ifndef slic3r_SupportMaterial_hpp_
|
#ifndef slic3r_SupportMaterial_hpp_
|
||||||
#define slic3r_SupportMaterial_hpp_
|
#define slic3r_SupportMaterial_hpp_
|
||||||
|
|
||||||
#include "../Flow.hpp"
|
#include "Flow.hpp"
|
||||||
#include "../PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
#include "../Slicing.hpp"
|
#include "Slicing.hpp"
|
||||||
|
|
||||||
#include "SupportLayer.hpp"
|
|
||||||
#include "SupportParameters.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class PrintObject;
|
class PrintObject;
|
||||||
|
class PrintConfig;
|
||||||
|
class PrintObjectConfig;
|
||||||
|
|
||||||
// This class manages raft and supports for a single PrintObject.
|
// This class manages raft and supports for a single PrintObject.
|
||||||
// Instantiated by Slic3r::Print::Object->_support_material()
|
// Instantiated by Slic3r::Print::Object->_support_material()
|
||||||
|
@ -18,6 +17,142 @@ class PrintObject;
|
||||||
// the parameters of the raft to determine the 1st layer height and thickness.
|
// the parameters of the raft to determine the 1st layer height and thickness.
|
||||||
class PrintObjectSupportMaterial
|
class PrintObjectSupportMaterial
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
// Support layer type to be used by MyLayer. This type carries a much more detailed information
|
||||||
|
// about the support layer type than the final support layers stored in a PrintObject.
|
||||||
|
enum SupporLayerType {
|
||||||
|
sltUnknown = 0,
|
||||||
|
// Ratft base layer, to be printed with the support material.
|
||||||
|
sltRaftBase,
|
||||||
|
// Raft interface layer, to be printed with the support interface material.
|
||||||
|
sltRaftInterface,
|
||||||
|
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
|
||||||
|
sltBottomContact,
|
||||||
|
// Dense interface layer, to be printed with the support interface material.
|
||||||
|
// This layer is separated from an object by an sltBottomContact layer.
|
||||||
|
sltBottomInterface,
|
||||||
|
// Sparse base support layer, to be printed with a support material.
|
||||||
|
sltBase,
|
||||||
|
// Dense interface layer, to be printed with the support interface material.
|
||||||
|
// This layer is separated from an object with sltTopContact layer.
|
||||||
|
sltTopInterface,
|
||||||
|
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
|
||||||
|
sltTopContact,
|
||||||
|
// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
|
||||||
|
sltIntermediate,
|
||||||
|
};
|
||||||
|
|
||||||
|
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
|
||||||
|
// information about the support layer than the layers stored in the PrintObject, mainly
|
||||||
|
// the MyLayer is aware of the bridging flow and the interface gaps between the object and the support.
|
||||||
|
class MyLayer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void reset() {
|
||||||
|
*this = MyLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const MyLayer &layer2) const {
|
||||||
|
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
|
||||||
|
bool operator<(const MyLayer &layer2) const {
|
||||||
|
if (print_z < layer2.print_z) {
|
||||||
|
return true;
|
||||||
|
} else if (print_z == layer2.print_z) {
|
||||||
|
if (height > layer2.height)
|
||||||
|
return true;
|
||||||
|
else if (height == layer2.height) {
|
||||||
|
// Bridging layers first.
|
||||||
|
return bridging && ! layer2.bridging;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void merge(MyLayer &&rhs) {
|
||||||
|
// The union_() does not support move semantic yet, but maybe one day it will.
|
||||||
|
this->polygons = union_(this->polygons, std::move(rhs.polygons));
|
||||||
|
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
|
||||||
|
if (! dst || dst->empty())
|
||||||
|
dst = std::move(src);
|
||||||
|
else if (src && ! src->empty())
|
||||||
|
*dst = union_(*dst, std::move(*src));
|
||||||
|
};
|
||||||
|
merge(this->contact_polygons, rhs.contact_polygons);
|
||||||
|
merge(this->overhang_polygons, rhs.overhang_polygons);
|
||||||
|
merge(this->enforcer_polygons, rhs.enforcer_polygons);
|
||||||
|
rhs.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
|
||||||
|
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
|
||||||
|
coordf_t bottom_print_z() const { return print_z - height; }
|
||||||
|
|
||||||
|
// To sort the extremes of top / bottom interface layers.
|
||||||
|
coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; }
|
||||||
|
|
||||||
|
SupporLayerType layer_type { sltUnknown };
|
||||||
|
// Z used for printing, in unscaled coordinates.
|
||||||
|
coordf_t print_z { 0 };
|
||||||
|
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
|
||||||
|
// otherwise bottom_z + gap + height = print_z.
|
||||||
|
coordf_t bottom_z { 0 };
|
||||||
|
// Layer height in unscaled coordinates.
|
||||||
|
coordf_t height { 0 };
|
||||||
|
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
|
||||||
|
// If this is not a contact layer, it will be set to size_t(-1).
|
||||||
|
size_t idx_object_layer_above { size_t(-1) };
|
||||||
|
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
|
||||||
|
// If this is not a contact layer, it will be set to size_t(-1).
|
||||||
|
size_t idx_object_layer_below { size_t(-1) };
|
||||||
|
// Use a bridging flow when printing this support layer.
|
||||||
|
bool bridging { false };
|
||||||
|
|
||||||
|
// Polygons to be filled by the support pattern.
|
||||||
|
Polygons polygons;
|
||||||
|
// Currently for the contact layers only.
|
||||||
|
std::unique_ptr<Polygons> contact_polygons;
|
||||||
|
std::unique_ptr<Polygons> overhang_polygons;
|
||||||
|
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
|
||||||
|
std::unique_ptr<Polygons> enforcer_polygons;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SupportParams {
|
||||||
|
Flow first_layer_flow;
|
||||||
|
Flow support_material_flow;
|
||||||
|
Flow support_material_interface_flow;
|
||||||
|
Flow support_material_bottom_interface_flow;
|
||||||
|
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
|
||||||
|
bool can_merge_support_regions;
|
||||||
|
|
||||||
|
coordf_t support_layer_height_min;
|
||||||
|
// coordf_t support_layer_height_max;
|
||||||
|
|
||||||
|
coordf_t gap_xy;
|
||||||
|
|
||||||
|
float base_angle;
|
||||||
|
float interface_angle;
|
||||||
|
coordf_t interface_spacing;
|
||||||
|
coordf_t support_expansion;
|
||||||
|
coordf_t interface_density;
|
||||||
|
coordf_t support_spacing;
|
||||||
|
coordf_t support_density;
|
||||||
|
|
||||||
|
InfillPattern base_fill_pattern;
|
||||||
|
InfillPattern interface_fill_pattern;
|
||||||
|
InfillPattern contact_fill_pattern;
|
||||||
|
bool with_sheath;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
|
||||||
|
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
|
||||||
|
// which would allocate layers by multiple chunks.
|
||||||
|
typedef std::deque<MyLayer> MyLayerStorage;
|
||||||
|
typedef std::vector<MyLayer*> MyLayersPtr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
|
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
|
||||||
|
|
||||||
|
@ -26,8 +161,8 @@ public:
|
||||||
// Has any support?
|
// Has any support?
|
||||||
bool has_support() const { return m_object_config->enable_support.value || m_object_config->enforce_support_layers; }
|
bool has_support() const { return m_object_config->enable_support.value || m_object_config->enforce_support_layers; }
|
||||||
bool build_plate_only() const { return this->has_support() && m_object_config->support_on_build_plate_only.value; }
|
bool build_plate_only() const { return this->has_support() && m_object_config->support_on_build_plate_only.value; }
|
||||||
|
// BBS
|
||||||
bool synchronize_layers() const { return m_slicing_params.soluble_interface && m_print_config->independent_support_layer_height.value; }
|
bool synchronize_layers() const { return /*m_slicing_params.soluble_interface && */!m_print_config->independent_support_layer_height.value; }
|
||||||
bool has_contact_loops() const { return m_object_config->support_interface_loop_pattern.value; }
|
bool has_contact_loops() const { return m_object_config->support_interface_loop_pattern.value; }
|
||||||
|
|
||||||
// Generate support material for the object.
|
// Generate support material for the object.
|
||||||
|
@ -36,47 +171,63 @@ public:
|
||||||
void generate(PrintObject &object);
|
void generate(PrintObject &object);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using SupportGeneratorLayersPtr = FFFSupport::SupportGeneratorLayersPtr;
|
|
||||||
using SupportGeneratorLayerStorage = FFFSupport::SupportGeneratorLayerStorage;
|
|
||||||
using SupportParameters = FFFSupport::SupportParameters;
|
|
||||||
|
|
||||||
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
|
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
|
||||||
|
|
||||||
// Generate top contact layers supporting overhangs.
|
// Generate top contact layers supporting overhangs.
|
||||||
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
|
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
|
||||||
// If supports over bed surface only are requested, don't generate contact layers over an object.
|
// If supports over bed surface only are requested, don't generate contact layers over an object.
|
||||||
SupportGeneratorLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const;
|
MyLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, MyLayerStorage &layer_storage) const;
|
||||||
|
|
||||||
// Generate bottom contact layers supporting the top contact layers.
|
// Generate bottom contact layers supporting the top contact layers.
|
||||||
// For a soluble interface material synchronize the layer heights with the object,
|
// For a soluble interface material synchronize the layer heights with the object,
|
||||||
// otherwise set the layer height to a bridging flow of a support interface nozzle.
|
// otherwise set the layer height to a bridging flow of a support interface nozzle.
|
||||||
SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas(
|
MyLayersPtr bottom_contact_layers_and_layer_support_areas(
|
||||||
const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
|
const PrintObject &object, const MyLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
|
||||||
SupportGeneratorLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
|
MyLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
|
||||||
|
|
||||||
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
|
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
|
||||||
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const;
|
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
|
||||||
|
|
||||||
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
|
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
|
||||||
SupportGeneratorLayersPtr raft_and_intermediate_support_layers(
|
MyLayersPtr raft_and_intermediate_support_layers(
|
||||||
const PrintObject &object,
|
const PrintObject &object,
|
||||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
const MyLayersPtr &bottom_contacts,
|
||||||
const SupportGeneratorLayersPtr &top_contacts,
|
const MyLayersPtr &top_contacts,
|
||||||
SupportGeneratorLayerStorage &layer_storage) const;
|
MyLayerStorage &layer_storage) const;
|
||||||
|
|
||||||
// Fill in the base layers with polygons.
|
// Fill in the base layers with polygons.
|
||||||
void generate_base_layers(
|
void generate_base_layers(
|
||||||
const PrintObject &object,
|
const PrintObject &object,
|
||||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
const MyLayersPtr &bottom_contacts,
|
||||||
const SupportGeneratorLayersPtr &top_contacts,
|
const MyLayersPtr &top_contacts,
|
||||||
SupportGeneratorLayersPtr &intermediate_layers,
|
MyLayersPtr &intermediate_layers,
|
||||||
const std::vector<Polygons> &layer_support_areas) const;
|
const std::vector<Polygons> &layer_support_areas) const;
|
||||||
|
|
||||||
|
// Generate raft layers, also expand the 1st support layer
|
||||||
|
// in case there is no raft layer to improve support adhesion.
|
||||||
|
MyLayersPtr generate_raft_base(
|
||||||
|
const PrintObject &object,
|
||||||
|
const MyLayersPtr &top_contacts,
|
||||||
|
const MyLayersPtr &interface_layers,
|
||||||
|
const MyLayersPtr &base_interface_layers,
|
||||||
|
const MyLayersPtr &base_layers,
|
||||||
|
MyLayerStorage &layer_storage) const;
|
||||||
|
|
||||||
|
// Turn some of the base layers into base interface layers.
|
||||||
|
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
|
||||||
|
// extruder to improve adhesion of the soluble filament to the base.
|
||||||
|
std::pair<MyLayersPtr, MyLayersPtr> generate_interface_layers(
|
||||||
|
const MyLayersPtr &bottom_contacts,
|
||||||
|
const MyLayersPtr &top_contacts,
|
||||||
|
MyLayersPtr &intermediate_layers,
|
||||||
|
MyLayerStorage &layer_storage) const;
|
||||||
|
|
||||||
|
|
||||||
// Trim support layers by an object to leave a defined gap between
|
// Trim support layers by an object to leave a defined gap between
|
||||||
// the support volume and the object.
|
// the support volume and the object.
|
||||||
void trim_support_layers_by_object(
|
void trim_support_layers_by_object(
|
||||||
const PrintObject &object,
|
const PrintObject &object,
|
||||||
SupportGeneratorLayersPtr &support_layers,
|
MyLayersPtr &support_layers,
|
||||||
const coordf_t gap_extra_above,
|
const coordf_t gap_extra_above,
|
||||||
const coordf_t gap_extra_below,
|
const coordf_t gap_extra_below,
|
||||||
const coordf_t gap_xy) const;
|
const coordf_t gap_xy) const;
|
||||||
|
@ -86,14 +237,25 @@ private:
|
||||||
void clip_with_shape();
|
void clip_with_shape();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Produce the actual G-code.
|
||||||
|
void generate_toolpaths(
|
||||||
|
SupportLayerPtrs &support_layers,
|
||||||
|
const MyLayersPtr &raft_layers,
|
||||||
|
const MyLayersPtr &bottom_contacts,
|
||||||
|
const MyLayersPtr &top_contacts,
|
||||||
|
const MyLayersPtr &intermediate_layers,
|
||||||
|
const MyLayersPtr &interface_layers,
|
||||||
|
const MyLayersPtr &base_interface_layers) const;
|
||||||
|
|
||||||
// Following objects are not owned by SupportMaterial class.
|
// Following objects are not owned by SupportMaterial class.
|
||||||
|
const PrintObject *m_object;
|
||||||
const PrintConfig *m_print_config;
|
const PrintConfig *m_print_config;
|
||||||
const PrintObjectConfig *m_object_config;
|
const PrintObjectConfig *m_object_config;
|
||||||
// Pre-calculated parameters shared between the object slicer and the support generator,
|
// Pre-calculated parameters shared between the object slicer and the support generator,
|
||||||
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
|
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
|
||||||
SlicingParameters m_slicing_params;
|
SlicingParameters m_slicing_params;
|
||||||
// Various precomputed support parameters to be shared with external functions.
|
// Various precomputed support parameters to be shared with external functions.
|
||||||
SupportParameters m_support_params;
|
SupportParams m_support_params;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -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 ®ion = 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
|
|
|
@ -9,10 +9,142 @@ namespace Slic3r {
|
||||||
class PrintObject;
|
class PrintObject;
|
||||||
enum InfillPattern : int;
|
enum InfillPattern : int;
|
||||||
|
|
||||||
namespace FFFSupport {
|
|
||||||
|
|
||||||
struct SupportParameters {
|
struct SupportParameters {
|
||||||
SupportParameters(const PrintObject &object);
|
SupportParameters(const PrintObject &object)
|
||||||
|
{
|
||||||
|
const PrintConfig &print_config = object.print()->config();
|
||||||
|
const PrintObjectConfig &object_config = object.config();
|
||||||
|
const SlicingParameters &slicing_params = object.slicing_parameters();
|
||||||
|
|
||||||
|
this->soluble_interface = slicing_params.soluble_interface;
|
||||||
|
this->soluble_interface_non_soluble_base =
|
||||||
|
// Zero z-gap between the overhangs and the support interface.
|
||||||
|
slicing_params.soluble_interface &&
|
||||||
|
// Interface extruder soluble.
|
||||||
|
object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) &&
|
||||||
|
// Base extruder: Either "print with active extruder" not soluble.
|
||||||
|
(object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1));
|
||||||
|
|
||||||
|
{
|
||||||
|
int num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value);
|
||||||
|
int num_bottom_interface_layers = object_config.support_interface_bottom_layers < 0 ?
|
||||||
|
num_top_interface_layers : object_config.support_interface_bottom_layers;
|
||||||
|
this->has_top_contacts = num_top_interface_layers > 0;
|
||||||
|
this->has_bottom_contacts = num_bottom_interface_layers > 0;
|
||||||
|
this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0;
|
||||||
|
this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0;
|
||||||
|
if (this->soluble_interface_non_soluble_base) {
|
||||||
|
// Try to support soluble dense interfaces with non-soluble dense interfaces.
|
||||||
|
this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2));
|
||||||
|
this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2));
|
||||||
|
} else {
|
||||||
|
this->num_top_base_interface_layers = 0;
|
||||||
|
this->num_bottom_base_interface_layers = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
|
||||||
|
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
|
||||||
|
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
|
||||||
|
this->raft_interface_flow = support_material_interface_flow;
|
||||||
|
|
||||||
|
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
||||||
|
this->support_layer_height_min = scaled<coord_t>(0.01);
|
||||||
|
for (auto lh : print_config.min_layer_height.values)
|
||||||
|
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh));
|
||||||
|
for (auto layer : object.layers())
|
||||||
|
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height));
|
||||||
|
|
||||||
|
if (object_config.support_interface_top_layers.value == 0) {
|
||||||
|
// No interface layers allowed, print everything with the base support pattern.
|
||||||
|
this->support_material_interface_flow = this->support_material_flow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
||||||
|
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
||||||
|
coordf_t external_perimeter_width = 0.;
|
||||||
|
coordf_t bridge_flow_ratio = 0;
|
||||||
|
for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) {
|
||||||
|
const PrintRegion ®ion = object.printing_region(region_id);
|
||||||
|
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width()));
|
||||||
|
bridge_flow_ratio += region.config().bridge_flow;
|
||||||
|
}
|
||||||
|
this->gap_xy = object_config.support_object_xy_distance;//.get_abs_value(external_perimeter_width);
|
||||||
|
bridge_flow_ratio /= object.num_printing_regions();
|
||||||
|
|
||||||
|
this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ?
|
||||||
|
this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
|
||||||
|
Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter());
|
||||||
|
|
||||||
|
this->can_merge_support_regions = object_config.support_filament.value == object_config.support_interface_filament.value;
|
||||||
|
if (!this->can_merge_support_regions && (object_config.support_filament.value == 0 || object_config.support_interface_filament.value == 0)) {
|
||||||
|
// One of the support extruders is of "don't care" type.
|
||||||
|
auto object_extruders = object.object_extruders();
|
||||||
|
if (object_extruders.size() == 1 &&
|
||||||
|
*object_extruders.begin() == std::max<unsigned int>(object_config.support_filament.value, object_config.support_interface_filament.value))
|
||||||
|
// Object is printed with the same extruder as the support.
|
||||||
|
this->can_merge_support_regions = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
double interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing();
|
||||||
|
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
|
||||||
|
double raft_interface_spacing = object_config.support_interface_spacing.value + this->raft_interface_flow.spacing();
|
||||||
|
this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing);
|
||||||
|
double support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing();
|
||||||
|
this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing);
|
||||||
|
if (object_config.support_interface_top_layers.value == 0) {
|
||||||
|
// No interface layers allowed, print everything with the base support pattern.
|
||||||
|
this->interface_density = this->support_density;
|
||||||
|
}
|
||||||
|
|
||||||
|
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
|
||||||
|
this->with_sheath = false;//object_config.support_material_with_sheath;
|
||||||
|
this->base_fill_pattern =
|
||||||
|
support_pattern == smpHoneycomb ? ipHoneycomb :
|
||||||
|
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
|
||||||
|
this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
|
||||||
|
this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase;
|
||||||
|
this->contact_fill_pattern =
|
||||||
|
(object_config.support_interface_pattern == smipAuto && slicing_params.soluble_interface) ||
|
||||||
|
object_config.support_interface_pattern == smipConcentric ?
|
||||||
|
ipConcentric :
|
||||||
|
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
|
||||||
|
|
||||||
|
this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value));
|
||||||
|
this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.));
|
||||||
|
this->raft_angle_1st_layer = 0.f;
|
||||||
|
this->raft_angle_base = 0.f;
|
||||||
|
this->raft_angle_interface = 0.f;
|
||||||
|
if (slicing_params.base_raft_layers > 1) {
|
||||||
|
assert(slicing_params.raft_layers() >= 4);
|
||||||
|
// There are all raft layer types (1st layer, base, interface & contact layers) available.
|
||||||
|
this->raft_angle_1st_layer = this->interface_angle;
|
||||||
|
this->raft_angle_base = this->base_angle;
|
||||||
|
this->raft_angle_interface = this->interface_angle;
|
||||||
|
if ((slicing_params.interface_raft_layers & 1) == 0)
|
||||||
|
// Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface.
|
||||||
|
this->raft_angle_interface += float(0.5 * M_PI);
|
||||||
|
} else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) {
|
||||||
|
assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3);
|
||||||
|
// 1st layer, interface & contact layers available.
|
||||||
|
this->raft_angle_1st_layer = this->base_angle;
|
||||||
|
this->raft_angle_interface = this->interface_angle + 0.5 * M_PI;
|
||||||
|
} else if (slicing_params.interface_raft_layers == 1) {
|
||||||
|
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
|
||||||
|
assert(slicing_params.base_raft_layers == 0);
|
||||||
|
assert(slicing_params.interface_raft_layers == 1);
|
||||||
|
assert(slicing_params.raft_layers() == 1);
|
||||||
|
this->raft_angle_1st_layer = float(0.5 * M_PI);
|
||||||
|
this->raft_angle_interface = this->raft_angle_1st_layer;
|
||||||
|
} else {
|
||||||
|
// No raft.
|
||||||
|
assert(slicing_params.base_raft_layers == 0);
|
||||||
|
assert(slicing_params.interface_raft_layers == 0);
|
||||||
|
assert(slicing_params.raft_layers() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI;
|
||||||
|
}
|
||||||
|
|
||||||
// Both top / bottom contacts and interfaces are soluble.
|
// Both top / bottom contacts and interfaces are soluble.
|
||||||
bool soluble_interface;
|
bool soluble_interface;
|
||||||
|
@ -89,8 +221,6 @@ struct SupportParameters {
|
||||||
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
|
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FFFSupport
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif /* slic3r_SupportParameters_hpp_ */
|
#endif /* slic3r_SupportParameters_hpp_ */
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include <tbb/parallel_for.h>
|
#include <tbb/parallel_for.h>
|
||||||
#include <tbb/task_group.h>
|
#include <tbb/task_group.h>
|
||||||
|
|
||||||
namespace Slic3r::FFFTreeSupport
|
namespace Slic3r::TreeSupport3D
|
||||||
{
|
{
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
@ -871,4 +871,4 @@ std::vector<std::pair<TreeModelVolumes::RadiusLayerPair, std::reference_wrapper<
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r::FFFTreeSupport
|
} // namespace Slic3r::TreeSupport3D
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace Slic3r
|
||||||
class BuildVolume;
|
class BuildVolume;
|
||||||
class PrintObject;
|
class PrintObject;
|
||||||
|
|
||||||
namespace FFFTreeSupport
|
namespace TreeSupport3D
|
||||||
{
|
{
|
||||||
|
|
||||||
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
|
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
|
||||||
|
@ -548,7 +548,7 @@ private:
|
||||||
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FFFTreeSupport
|
} // namespace TreeSupport3D
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif //slic3r_TreeModelVolumes_hpp
|
#endif //slic3r_TreeModelVolumes_hpp
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,299 +1,511 @@
|
||||||
// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine.
|
#ifndef TREESUPPORT_H
|
||||||
// Original source of Thomas Rahm's tree supports:
|
#define TREESUPPORT_H
|
||||||
// https://github.com/ThomasRahm/CuraEngine
|
|
||||||
//
|
|
||||||
// Original CuraEngine copyright:
|
|
||||||
// Copyright (c) 2021 Ultimaker B.V.
|
|
||||||
// CuraEngine is released under the terms of the AGPLv3 or higher.
|
|
||||||
|
|
||||||
#ifndef slic3r_TreeSupport_hpp
|
#include <forward_list>
|
||||||
#define slic3r_TreeSupport_hpp
|
#include <unordered_set>
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
#include "Point.hpp"
|
||||||
|
#include "Slicing.hpp"
|
||||||
|
#include "MinimumSpanningTree.hpp"
|
||||||
|
#include "tbb/concurrent_unordered_map.h"
|
||||||
|
#include "Flow.hpp"
|
||||||
|
#include "PrintConfig.hpp"
|
||||||
|
#include "Fill/Lightning/Generator.hpp"
|
||||||
|
|
||||||
#include "SupportLayer.hpp"
|
#ifndef SQ
|
||||||
#include "TreeModelVolumes.hpp"
|
#define SQ(x) ((x)*(x))
|
||||||
#include "TreeSupportCommon.hpp"
|
#endif
|
||||||
|
|
||||||
#include "../BoundingBox.hpp"
|
|
||||||
#include "../Point.hpp"
|
|
||||||
#include "../Utils.hpp"
|
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
// #define TREE_SUPPORT_SHOW_ERRORS
|
|
||||||
|
|
||||||
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
|
||||||
// The various stages of the process can be weighted differently in the progress bar.
|
|
||||||
// These weights are obtained experimentally using a small sample size. Sensible weights can differ drastically based on the assumed default settings and model.
|
|
||||||
#define TREE_PROGRESS_TOTAL 10000
|
|
||||||
#define TREE_PROGRESS_PRECALC_COLL TREE_PROGRESS_TOTAL * 0.1
|
|
||||||
#define TREE_PROGRESS_PRECALC_AVO TREE_PROGRESS_TOTAL * 0.4
|
|
||||||
#define TREE_PROGRESS_GENERATE_NODES TREE_PROGRESS_TOTAL * 0.1
|
|
||||||
#define TREE_PROGRESS_AREA_CALC TREE_PROGRESS_TOTAL * 0.3
|
|
||||||
#define TREE_PROGRESS_DRAW_AREAS TREE_PROGRESS_TOTAL * 0.1
|
|
||||||
#define TREE_PROGRESS_GENERATE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
|
|
||||||
#define TREE_PROGRESS_SMOOTH_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
|
|
||||||
#define TREE_PROGRESS_FINALIZE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
|
|
||||||
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
|
||||||
|
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
{
|
{
|
||||||
|
|
||||||
// Forward declarations
|
|
||||||
class Print;
|
|
||||||
class PrintObject;
|
class PrintObject;
|
||||||
struct SlicingParameters;
|
class TreeSupport;
|
||||||
|
class SupportLayer;
|
||||||
|
|
||||||
namespace FFFTreeSupport
|
struct LayerHeightData
|
||||||
{
|
{
|
||||||
|
coordf_t print_z = 0;
|
||||||
|
coordf_t height = 0;
|
||||||
|
size_t next_layer_nr = 0;
|
||||||
|
LayerHeightData() = default;
|
||||||
|
LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {}
|
||||||
|
};
|
||||||
|
|
||||||
// The number of vertices in each circle.
|
struct TreeNode {
|
||||||
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
|
Vec3f pos;
|
||||||
|
std::vector<int> children; // index of children in the storing vector
|
||||||
struct AreaIncreaseSettings
|
std::vector<int> parents; // index of parents in the storing vector
|
||||||
{
|
TreeNode(Point pt, float z) {
|
||||||
AreaIncreaseSettings(
|
pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z };
|
||||||
TreeModelVolumes::AvoidanceType type = TreeModelVolumes::AvoidanceType::Fast, coord_t increase_speed = 0,
|
|
||||||
bool increase_radius = false, bool no_error = false, bool use_min_distance = false, bool move = false) :
|
|
||||||
increase_speed{ increase_speed }, type{ type }, increase_radius{ increase_radius }, no_error{ no_error }, use_min_distance{ use_min_distance }, move{ move } {}
|
|
||||||
|
|
||||||
coord_t increase_speed;
|
|
||||||
// Packing for smaller memory footprint of SupportElementState && SupportElementMerging
|
|
||||||
TreeModelVolumes::AvoidanceType type;
|
|
||||||
bool increase_radius : 1;
|
|
||||||
bool no_error : 1;
|
|
||||||
bool use_min_distance : 1;
|
|
||||||
bool move : 1;
|
|
||||||
bool operator==(const AreaIncreaseSettings& other) const
|
|
||||||
{
|
|
||||||
return type == other.type &&
|
|
||||||
increase_speed == other.increase_speed &&
|
|
||||||
increase_radius == other.increase_radius &&
|
|
||||||
no_error == other.no_error &&
|
|
||||||
use_min_distance == other.use_min_distance &&
|
|
||||||
move == other.move;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TREE_SUPPORTS_TRACK_LOST
|
/*!
|
||||||
|
* \brief Lazily generates tree guidance volumes.
|
||||||
|
*
|
||||||
|
* \warning This class is not currently thread-safe and should not be accessed in OpenMP blocks
|
||||||
|
*/
|
||||||
|
class TreeSupportData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TreeSupportData() = default;
|
||||||
|
/*!
|
||||||
|
* \brief Construct the TreeSupportData object
|
||||||
|
*
|
||||||
|
* \param xy_distance The required clearance between the model and the
|
||||||
|
* tree branches.
|
||||||
|
* \param max_move The maximum allowable movement between nodes on
|
||||||
|
* adjacent layers
|
||||||
|
* \param radius_sample_resolution Sample size used to round requested node radii.
|
||||||
|
* \param collision_resolution
|
||||||
|
*/
|
||||||
|
TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution);
|
||||||
|
|
||||||
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
|
TreeSupportData(TreeSupportData&&) = default;
|
||||||
struct SupportElementStateBits {
|
TreeSupportData& operator=(TreeSupportData&&) = default;
|
||||||
SupportElementStateBits() :
|
|
||||||
to_buildplate(false),
|
TreeSupportData(const TreeSupportData&) = delete;
|
||||||
to_model_gracious(false),
|
TreeSupportData& operator=(const TreeSupportData&) = delete;
|
||||||
use_min_xy_dist(false),
|
|
||||||
supports_roof(false),
|
/*!
|
||||||
can_use_safe_radius(false),
|
* \brief Creates the areas that have to be avoided by the tree's branches.
|
||||||
skip_ovalisation(false),
|
*
|
||||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
* The result is a 2D area that would cause nodes of radius \p radius to
|
||||||
lost(false),
|
* collide with the model.
|
||||||
verylost(false),
|
*
|
||||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
* \param radius The radius of the node of interest
|
||||||
deleted(false),
|
* \param layer The layer of interest
|
||||||
marked(false)
|
* \return Polygons object
|
||||||
|
*/
|
||||||
|
const ExPolygons& get_collision(coordf_t radius, size_t layer_idx) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Creates the areas that have to be avoided by the tree's branches
|
||||||
|
* in order to reach the build plate.
|
||||||
|
*
|
||||||
|
* The result is a 2D area that would cause nodes of radius \p radius to
|
||||||
|
* collide with the model or be unable to reach the build platform.
|
||||||
|
*
|
||||||
|
* The input collision areas are inset by the maximum move distance and
|
||||||
|
* propagated upwards.
|
||||||
|
*
|
||||||
|
* \param radius The radius of the node of interest
|
||||||
|
* \param layer The layer of interest
|
||||||
|
* \return Polygons object
|
||||||
|
*/
|
||||||
|
const ExPolygons& get_avoidance(coordf_t radius, size_t layer_idx, int recursions=0) const;
|
||||||
|
|
||||||
|
Polygons get_contours(size_t layer_nr) const;
|
||||||
|
Polygons get_contours_with_holes(size_t layer_nr) const;
|
||||||
|
|
||||||
|
std::vector<LayerHeightData> layer_heights;
|
||||||
|
|
||||||
|
std::vector<TreeNode> tree_nodes;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
* \brief Convenience typedef for the keys to the caches
|
||||||
|
*/
|
||||||
|
struct RadiusLayerPair {
|
||||||
|
coordf_t radius;
|
||||||
|
size_t layer_nr;
|
||||||
|
int recursions;
|
||||||
|
|
||||||
|
};
|
||||||
|
struct RadiusLayerPairEquality {
|
||||||
|
constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const {
|
||||||
|
return _Left.radius == _Right.radius && _Left.layer_nr == _Right.layer_nr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct RadiusLayerPairHash {
|
||||||
|
size_t operator()(const RadiusLayerPair& elem) const {
|
||||||
|
return std::hash<coord_t>()(elem.radius) ^ std::hash<coord_t>()(elem.layer_nr * 7919);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Round \p radius upwards to a multiple of m_radius_sample_resolution
|
||||||
|
*
|
||||||
|
* \param radius The radius of the node of interest
|
||||||
|
*/
|
||||||
|
coordf_t ceil_radius(coordf_t radius) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Calculate the collision areas at the radius and layer indicated
|
||||||
|
* by \p key.
|
||||||
|
*
|
||||||
|
* \param key The radius and layer of the node of interest
|
||||||
|
*/
|
||||||
|
const ExPolygons& calculate_collision(const RadiusLayerPair& key) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Calculate the avoidance areas at the radius and layer indicated
|
||||||
|
* by \p key.
|
||||||
|
*
|
||||||
|
* \param key The radius and layer of the node of interest
|
||||||
|
*/
|
||||||
|
const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool is_slim = false;
|
||||||
|
/*!
|
||||||
|
* \brief The required clearance between the model and the tree branches
|
||||||
|
*/
|
||||||
|
coordf_t m_xy_distance;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The maximum distance that the centrepoint of a tree branch may
|
||||||
|
* move in consequtive layers
|
||||||
|
*/
|
||||||
|
coordf_t m_max_move;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Sample resolution for radius values.
|
||||||
|
*
|
||||||
|
* The radius will be rounded (upwards) to multiples of this value before
|
||||||
|
* calculations are done when collision, avoidance and internal model
|
||||||
|
* Polygons are requested.
|
||||||
|
*/
|
||||||
|
coordf_t m_radius_sample_resolution;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Storage for layer outlines of the meshes.
|
||||||
|
*/
|
||||||
|
std::vector<ExPolygons> m_layer_outlines;
|
||||||
|
|
||||||
|
// union contours of all layers below
|
||||||
|
std::vector<ExPolygons> m_layer_outlines_below;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Caches for the collision, avoidance and internal model polygons
|
||||||
|
* at given radius and layer indices.
|
||||||
|
*
|
||||||
|
* These are mutable to allow modification from const function. This is
|
||||||
|
* generally considered OK as the functions are still logically const
|
||||||
|
* (ie there is no difference in behaviour for the user betweeen
|
||||||
|
* calculating the values each time vs caching the results).
|
||||||
|
*
|
||||||
|
* coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for.
|
||||||
|
* So we change to tbb::concurrent_unordered_map
|
||||||
|
*/
|
||||||
|
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_collision_cache;
|
||||||
|
mutable tbb::concurrent_unordered_map<RadiusLayerPair, ExPolygons, RadiusLayerPairHash, RadiusLayerPairEquality> m_avoidance_cache;
|
||||||
|
|
||||||
|
friend TreeSupport;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LineHash {
|
||||||
|
size_t operator()(const Line& line) const {
|
||||||
|
return (std::hash<coord_t>()(line.a(0)) ^ std::hash<coord_t>()(line.b(1))) * 102 +
|
||||||
|
(std::hash<coord_t>()(line.a(1)) ^ std::hash<coord_t>()(line.b(0))) * 10222;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Generates a tree structure to support your models.
|
||||||
|
*/
|
||||||
|
class TreeSupport
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* \brief Creates an instance of the tree support generator.
|
||||||
|
*
|
||||||
|
* \param storage The data storage to get global settings from.
|
||||||
|
*/
|
||||||
|
TreeSupport(PrintObject& object, const SlicingParameters &slicing_params);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Create the areas that need support.
|
||||||
|
*
|
||||||
|
* These areas are stored inside the given SliceDataStorage object.
|
||||||
|
* \param storage The data storage where the mesh data is gotten from and
|
||||||
|
* where the resulting support areas are stored.
|
||||||
|
*/
|
||||||
|
void generate();
|
||||||
|
|
||||||
|
void detect_overhangs(bool detect_first_sharp_tail_only=false);
|
||||||
|
|
||||||
|
enum NodeType {
|
||||||
|
eCircle,
|
||||||
|
eSquare,
|
||||||
|
ePolygon
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Represents the metadata of a node in the tree.
|
||||||
|
*/
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
static constexpr Node* NO_PARENT = nullptr;
|
||||||
|
|
||||||
|
Node()
|
||||||
|
: distance_to_top(0)
|
||||||
|
, position(Point(0, 0))
|
||||||
|
, obj_layer_nr(0)
|
||||||
|
, support_roof_layers_below(0)
|
||||||
|
, support_floor_layers_above(0)
|
||||||
|
, to_buildplate(true)
|
||||||
|
, parent(nullptr)
|
||||||
|
, print_z(0.0)
|
||||||
|
, height(0.0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/*!
|
// when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height;
|
||||||
* \brief The element trys to reach the buildplate
|
Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
|
||||||
*/
|
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0)
|
||||||
bool to_buildplate : 1;
|
: distance_to_top(distance_to_top)
|
||||||
|
, position(position)
|
||||||
|
, obj_layer_nr(obj_layer_nr)
|
||||||
|
, support_roof_layers_below(support_roof_layers_below)
|
||||||
|
, support_floor_layers_above(0)
|
||||||
|
, to_buildplate(to_buildplate)
|
||||||
|
, parent(parent)
|
||||||
|
, print_z(print_z_)
|
||||||
|
, height(height_)
|
||||||
|
, dist_mm_to_top(dist_mm_to_top_)
|
||||||
|
{
|
||||||
|
if (parent) {
|
||||||
|
type = parent->type;
|
||||||
|
overhang = parent->overhang;
|
||||||
|
if (dist_mm_to_top==0)
|
||||||
|
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
|
||||||
|
parent->child = this;
|
||||||
|
for (auto& neighbor : parent->merged_neighbours)
|
||||||
|
neighbor->child = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node.
|
||||||
* \brief Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
|
~Node()
|
||||||
*/
|
{
|
||||||
bool to_model_gracious : 1;
|
parent = nullptr;
|
||||||
|
merged_neighbours.clear();
|
||||||
|
}
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Whether the min_xy_distance can be used to get avoidance or similar. Will only be true if support_xy_overrides_z=Z overrides X/Y.
|
* \brief The number of layers to go to the top of this branch.
|
||||||
*/
|
* Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded.
|
||||||
bool use_min_xy_dist : 1;
|
*/
|
||||||
|
int distance_to_top;
|
||||||
|
coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief True if this Element or any parent (element above) provides support to a support roof.
|
* \brief The position of this node on the layer.
|
||||||
*/
|
*/
|
||||||
bool supports_roof : 1;
|
Point position;
|
||||||
|
Point movement; // movement towards neighbor center or outline
|
||||||
|
mutable double radius = 0.0;
|
||||||
|
mutable double max_move_dist = 0.0;
|
||||||
|
NodeType type = eCircle;
|
||||||
|
bool is_merged = false; // this node is generated by merging upper nodes
|
||||||
|
bool is_corner = false;
|
||||||
|
bool is_processed = false;
|
||||||
|
const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief An influence area is considered safe when it can use the holefree avoidance <=> It will not have to encounter holes on its way downward.
|
* \brief The direction of the skin lines above the tip of the branch.
|
||||||
*/
|
*
|
||||||
bool can_use_safe_radius : 1;
|
* This determines in which direction we should reduce the width of the
|
||||||
|
* branch.
|
||||||
|
*/
|
||||||
|
bool skin_direction;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Skip the ovalisation to parent and children when generating the final circles.
|
* \brief The number of support roof layers below this one.
|
||||||
*/
|
*
|
||||||
bool skip_ovalisation : 1;
|
* When a contact point is created, it is determined whether the mesh
|
||||||
|
* needs to be supported with support roof or not, since that is a
|
||||||
|
* per-mesh setting. This is stored in this variable in order to track
|
||||||
|
* how far we need to extend that support roof downwards.
|
||||||
|
*/
|
||||||
|
int support_roof_layers_below;
|
||||||
|
int support_floor_layers_above;
|
||||||
|
int obj_layer_nr;
|
||||||
|
|
||||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
/*!
|
||||||
// Likely a lost branch, debugging information.
|
* \brief Whether to try to go towards the build plate.
|
||||||
bool lost : 1;
|
*
|
||||||
bool verylost : 1;
|
* If the node is inside the collision areas, it has no choice but to go
|
||||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
* towards the model. If it is not inside the collision areas, it must
|
||||||
|
* go towards the build plate to prevent a scar on the surface.
|
||||||
|
*/
|
||||||
|
bool to_buildplate;
|
||||||
|
|
||||||
// Not valid anymore, to be deleted.
|
/*!
|
||||||
bool deleted : 1;
|
* \brief The originating node for this one, one layer higher.
|
||||||
|
*
|
||||||
|
* In order to prune branches that can't have any support (because they
|
||||||
|
* can't be on the model and the path to the buildplate isn't clear),
|
||||||
|
* the entire branch needs to be known.
|
||||||
|
*/
|
||||||
|
Node *parent;
|
||||||
|
Node *child = nullptr;
|
||||||
|
|
||||||
// General purpose flag marking a visited element.
|
/*!
|
||||||
bool marked : 1;
|
* \brief All neighbours (on the same layer) that where merged into this node.
|
||||||
};
|
*
|
||||||
|
* In order to prune branches that can't have any support (because they
|
||||||
|
* can't be on the model and the path to the buildplate isn't clear),
|
||||||
|
* the entire branch needs to be known.
|
||||||
|
*/
|
||||||
|
std::list<Node*> merged_neighbours;
|
||||||
|
|
||||||
struct SupportElementState : public SupportElementStateBits
|
coordf_t print_z;
|
||||||
{
|
coordf_t height;
|
||||||
/*!
|
|
||||||
* \brief The layer this support elements wants reach
|
|
||||||
*/
|
|
||||||
LayerIndex target_height;
|
|
||||||
|
|
||||||
/*!
|
bool operator==(const Node& other) const
|
||||||
* \brief The position this support elements wants to support on layer=target_height
|
{
|
||||||
*/
|
return position == other.position;
|
||||||
Point target_position;
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
struct SupportParams
|
||||||
* \brief The next position this support elements wants to reach. NOTE: This is mainly a suggestion regarding direction inside the influence area.
|
|
||||||
*/
|
|
||||||
Point next_position;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief The next height this support elements wants to reach
|
|
||||||
*/
|
|
||||||
LayerIndex layer_idx;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief The Effective distance to top of this element regarding radius increases and collision calculations.
|
|
||||||
*/
|
|
||||||
uint32_t effective_radius_height;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief The amount of layers this element is below the topmost layer of this branch.
|
|
||||||
*/
|
|
||||||
uint32_t distance_to_top;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief The resulting center point around which a circle will be drawn later.
|
|
||||||
* Will be set by setPointsOnAreas
|
|
||||||
*/
|
|
||||||
Point result_on_layer { std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
|
|
||||||
bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
|
|
||||||
void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
|
|
||||||
/*!
|
|
||||||
* \brief The amount of extra radius we got from merging branches that could have reached the buildplate, but merged with ones that can not.
|
|
||||||
*/
|
|
||||||
coord_t increased_to_model_radius; // how much to model we increased only relevant for merging
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Counter about the times the elephant foot was increased. Can be fractions for merge reasons.
|
|
||||||
*/
|
|
||||||
double elephant_foot_increases;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief The element tries to not move until this dtt is reached, is set to 0 if the element had to move.
|
|
||||||
*/
|
|
||||||
uint32_t dont_move_until;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Settings used to increase the influence area to its current state.
|
|
||||||
*/
|
|
||||||
AreaIncreaseSettings last_area_increase;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Amount of roof layers that were not yet added, because the branch needed to move.
|
|
||||||
*/
|
|
||||||
uint32_t missing_roof_layers;
|
|
||||||
|
|
||||||
// called by increase_single_area() and increaseAreas()
|
|
||||||
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState &src)
|
|
||||||
{
|
{
|
||||||
SupportElementState dst{ src };
|
Flow first_layer_flow;
|
||||||
++ dst.distance_to_top;
|
Flow support_material_flow;
|
||||||
-- dst.layer_idx;
|
Flow support_material_interface_flow;
|
||||||
// set to invalid as we are a new node on a new layer
|
Flow support_material_bottom_interface_flow;
|
||||||
dst.result_on_layer_reset();
|
coordf_t support_extrusion_width;
|
||||||
dst.skip_ovalisation = false;
|
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
|
||||||
return dst;
|
bool can_merge_support_regions;
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; }
|
coordf_t support_layer_height_min;
|
||||||
};
|
// coordf_t support_layer_height_max;
|
||||||
|
|
||||||
/*!
|
coordf_t gap_xy;
|
||||||
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
|
|
||||||
* \param elem[in] The SupportElement one wants to know the effectiveDTT
|
|
||||||
* \return The Effective DTT.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] inline size_t getEffectiveDTT(const TreeSupportSettings &settings, const SupportElementState &elem)
|
|
||||||
{
|
|
||||||
return elem.effective_radius_height < settings.increase_radius_until_layer ?
|
|
||||||
(elem.distance_to_top < settings.increase_radius_until_layer ? elem.distance_to_top : settings.increase_radius_until_layer) :
|
|
||||||
elem.effective_radius_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
float base_angle;
|
||||||
* \brief Get the Radius, that this element will have.
|
float interface_angle;
|
||||||
* \param elem[in] The Element.
|
coordf_t interface_spacing;
|
||||||
* \return The radius the element has.
|
coordf_t interface_density;
|
||||||
*/
|
coordf_t support_spacing;
|
||||||
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
|
coordf_t support_density;
|
||||||
{
|
|
||||||
return settings.getRadius(getEffectiveDTT(settings, elem), elem.elephant_foot_increases);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
InfillPattern base_fill_pattern;
|
||||||
* \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.
|
InfillPattern interface_fill_pattern;
|
||||||
* \param elem[in] The Element.
|
InfillPattern contact_fill_pattern;
|
||||||
* \return The collision radius the element has.
|
bool with_sheath;
|
||||||
*/
|
const double thresh_big_overhang = SQ(scale_(10));
|
||||||
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
|
};
|
||||||
{
|
|
||||||
return settings.getRadius(elem.effective_radius_height, elem.elephant_foot_increases);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SupportElement
|
int avg_node_per_layer = 0;
|
||||||
{
|
float nodes_angle = 0;
|
||||||
using ParentIndices =
|
bool has_overhangs = false;
|
||||||
#ifdef NDEBUG
|
bool has_sharp_tails = false;
|
||||||
// To reduce memory allocation in release mode.
|
bool has_cantilever = false;
|
||||||
boost::container::small_vector<int32_t, 4>;
|
double max_cantilever_dist = 0;
|
||||||
#else // NDEBUG
|
SupportType support_type;
|
||||||
// To ease debugging.
|
SupportMaterialStyle support_style;
|
||||||
std::vector<int32_t>;
|
|
||||||
#endif // NDEBUG
|
|
||||||
|
|
||||||
// SupportElement(const SupportElementState &state) : SupportElementState(state) {}
|
std::unique_ptr<FillLightning::Generator> generator;
|
||||||
SupportElement(const SupportElementState &state, Polygons &&influence_area) : state(state), influence_area(std::move(influence_area)) {}
|
std::unordered_map<double, size_t> printZ_to_lightninglayer;
|
||||||
SupportElement(const SupportElementState &state, ParentIndices &&parents, Polygons &&influence_area) :
|
private:
|
||||||
state(state), parents(std::move(parents)), influence_area(std::move(influence_area)) {}
|
/*!
|
||||||
|
* \brief Generator for model collision, avoidance and internal guide volumes
|
||||||
|
*
|
||||||
|
* Lazily computes volumes as needed.
|
||||||
|
* \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks
|
||||||
|
*/
|
||||||
|
std::shared_ptr<TreeSupportData> m_ts_data;
|
||||||
|
PrintObject *m_object;
|
||||||
|
const PrintObjectConfig *m_object_config;
|
||||||
|
SlicingParameters m_slicing_params;
|
||||||
|
// Various precomputed support parameters to be shared with external functions.
|
||||||
|
SupportParams m_support_params;
|
||||||
|
size_t m_raft_layers = 0;
|
||||||
|
size_t m_highest_overhang_layer = 0;
|
||||||
|
std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees;
|
||||||
|
std::vector< std::unordered_map<Line, bool, LineHash>> m_mst_line_x_layer_contour_caches;
|
||||||
|
coordf_t MAX_BRANCH_RADIUS = 10.0;
|
||||||
|
coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
|
||||||
|
coordf_t MIN_BRANCH_RADIUS = 0.5;
|
||||||
|
float tree_support_branch_diameter_angle = 5.0;
|
||||||
|
bool is_strong = false;
|
||||||
|
bool is_slim = false;
|
||||||
|
bool with_infill = false;
|
||||||
|
|
||||||
SupportElementState state;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief All elements in the layer above the current one that are supported by this element
|
* \brief Polygons representing the limits of the printable area of the
|
||||||
|
* machine
|
||||||
*/
|
*/
|
||||||
ParentIndices parents;
|
ExPolygon m_machine_border;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The resulting influence area.
|
* \brief Draws circles around each node of the tree into the final support.
|
||||||
* Will only be set in the results of createLayerPathing, and will be nullptr inside!
|
*
|
||||||
|
* This also handles the areas that have to become support roof, support
|
||||||
|
* bottom, the Z distances, etc.
|
||||||
|
*
|
||||||
|
* \param storage[in, out] The settings storage to get settings from and to
|
||||||
|
* save the resulting support polygons to.
|
||||||
|
* \param contact_nodes The nodes to draw as support.
|
||||||
*/
|
*/
|
||||||
Polygons influence_area;
|
void draw_circles(const std::vector<std::vector<Node*>>& contact_nodes);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Drops down the nodes of the tree support towards the build plate.
|
||||||
|
*
|
||||||
|
* This is where the cleverness of tree support comes in: The nodes stay on
|
||||||
|
* their 2D layers but on the next layer they are slightly shifted. This
|
||||||
|
* causes them to move towards each other as they are copied to lower layers
|
||||||
|
* which ultimately results in a 3D tree.
|
||||||
|
*
|
||||||
|
* \param contact_nodes[in, out] The nodes in the space that need to be
|
||||||
|
* dropped down. The nodes are dropped to lower layers inside the same
|
||||||
|
* vector of layers.
|
||||||
|
*/
|
||||||
|
void drop_nodes(std::vector<std::vector<Node *>> &contact_nodes);
|
||||||
|
|
||||||
|
void smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes);
|
||||||
|
|
||||||
|
void adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes);
|
||||||
|
|
||||||
|
/*! BBS: MusangKing: maximum layer height
|
||||||
|
* \brief Optimize the generation of tree support by pre-planning the layer_heights
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes);
|
||||||
|
/*!
|
||||||
|
* \brief Creates points where support contacts the model.
|
||||||
|
*
|
||||||
|
* A set of points is created for each layer.
|
||||||
|
* \param mesh The mesh to get the overhang areas to support of.
|
||||||
|
* \param contact_nodes[out] A vector of mappings from contact points to
|
||||||
|
* their tree nodes.
|
||||||
|
* \param collision_areas For every layer, the areas where a generated
|
||||||
|
* contact point would immediately collide with the model due to the X/Y
|
||||||
|
* distance.
|
||||||
|
* \return For each layer, a list of points where the tree should connect
|
||||||
|
* with the model.
|
||||||
|
*/
|
||||||
|
void generate_contact_points(std::vector<std::vector<Node*>>& contact_nodes);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Add a node to the next layer.
|
||||||
|
*
|
||||||
|
* If a node is already at that position in the layer, the nodes are merged.
|
||||||
|
*/
|
||||||
|
void insert_dropped_node(std::vector<Node*>& nodes_layer, Node* node);
|
||||||
|
void create_tree_support_layers();
|
||||||
|
void generate_toolpaths();
|
||||||
|
Polygons spanning_tree_to_polygon(const std::vector<MinimumSpanningTree>& spanning_trees, Polygons layer_contours, int layer_nr);
|
||||||
|
Polygons contact_nodes_to_polygon(const std::vector<Node*>& contact_nodes, Polygons layer_contours, int layer_nr, std::vector<double>& radiis, std::vector<bool>& is_interface);
|
||||||
|
coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor);
|
||||||
|
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor);
|
||||||
|
|
||||||
|
// similar to SupportMaterial::trim_support_layers_by_object
|
||||||
|
Polygons get_trim_support_regions(
|
||||||
|
const PrintObject& object,
|
||||||
|
SupportLayer* support_layer_ptr,
|
||||||
|
const coordf_t gap_extra_above,
|
||||||
|
const coordf_t gap_extra_below,
|
||||||
|
const coordf_t gap_xy);
|
||||||
};
|
};
|
||||||
|
|
||||||
using SupportElements = std::deque<SupportElement>;
|
|
||||||
|
|
||||||
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
|
|
||||||
{
|
|
||||||
return support_element_radius(settings, elem.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElement &elem)
|
#endif /* TREESUPPORT_H */
|
||||||
{
|
|
||||||
return support_element_collision_radius(settings, elem.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace FFFTreeSupport
|
|
||||||
|
|
||||||
void fff_tree_support_generate(PrintObject &print_object, std::function<void()> throw_on_cancel = []{});
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
|
||||||
|
|
||||||
#endif /* slic3r_TreeSupport_hpp */
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,13 +9,17 @@
|
||||||
#ifndef slic3r_TreeSupport_hpp
|
#ifndef slic3r_TreeSupport_hpp
|
||||||
#define slic3r_TreeSupport_hpp
|
#define slic3r_TreeSupport_hpp
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include "SupportLayer.hpp"
|
||||||
#include "../Point.hpp"
|
|
||||||
#include "../BoundingBox.hpp"
|
|
||||||
#include "../Utils.hpp"
|
|
||||||
#include "TreeModelVolumes.hpp"
|
#include "TreeModelVolumes.hpp"
|
||||||
#include "TreeSupportCommon.hpp"
|
#include "TreeSupportCommon.hpp"
|
||||||
|
|
||||||
|
#include "../BoundingBox.hpp"
|
||||||
|
#include "../Point.hpp"
|
||||||
|
#include "../Utils.hpp"
|
||||||
|
|
||||||
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
|
|
||||||
// #define TREE_SUPPORT_SHOW_ERRORS
|
// #define TREE_SUPPORT_SHOW_ERRORS
|
||||||
|
|
||||||
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
||||||
|
@ -36,11 +40,9 @@ namespace Slic3r
|
||||||
{
|
{
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class TreeSupport;
|
|
||||||
class Print;
|
class Print;
|
||||||
class PrintObject;
|
class PrintObject;
|
||||||
class SupportGeneratorLayer;
|
struct SlicingParameters;
|
||||||
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
|
|
||||||
|
|
||||||
namespace TreeSupport3D
|
namespace TreeSupport3D
|
||||||
{
|
{
|
||||||
|
@ -90,7 +92,7 @@ struct SupportElementStateBits {
|
||||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
#endif // TREE_SUPPORTS_TRACK_LOST
|
||||||
deleted(false),
|
deleted(false),
|
||||||
marked(false)
|
marked(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The element trys to reach the buildplate
|
* \brief The element trys to reach the buildplate
|
||||||
|
@ -108,7 +110,7 @@ struct SupportElementStateBits {
|
||||||
bool use_min_xy_dist : 1;
|
bool use_min_xy_dist : 1;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief True if this Element or any parent provides support to a support roof.
|
* \brief True if this Element or any parent (element above) provides support to a support roof.
|
||||||
*/
|
*/
|
||||||
bool supports_roof : 1;
|
bool supports_roof : 1;
|
||||||
|
|
||||||
|
@ -137,10 +139,6 @@ struct SupportElementStateBits {
|
||||||
|
|
||||||
struct SupportElementState : public SupportElementStateBits
|
struct SupportElementState : public SupportElementStateBits
|
||||||
{
|
{
|
||||||
int type = 0;
|
|
||||||
coordf_t radius = 0;
|
|
||||||
float print_z = 0;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The layer this support elements wants reach
|
* \brief The layer this support elements wants reach
|
||||||
*/
|
*/
|
||||||
|
@ -175,7 +173,7 @@ struct SupportElementState : public SupportElementStateBits
|
||||||
* \brief The resulting center point around which a circle will be drawn later.
|
* \brief The resulting center point around which a circle will be drawn later.
|
||||||
* Will be set by setPointsOnAreas
|
* Will be set by setPointsOnAreas
|
||||||
*/
|
*/
|
||||||
Point result_on_layer{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
|
Point result_on_layer { std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
|
||||||
bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
|
bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
|
||||||
void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
|
void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
|
||||||
/*!
|
/*!
|
||||||
|
@ -189,7 +187,7 @@ struct SupportElementState : public SupportElementStateBits
|
||||||
double elephant_foot_increases;
|
double elephant_foot_increases;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move.
|
* \brief The element tries to not move until this dtt is reached, is set to 0 if the element had to move.
|
||||||
*/
|
*/
|
||||||
uint32_t dont_move_until;
|
uint32_t dont_move_until;
|
||||||
|
|
||||||
|
@ -204,18 +202,19 @@ struct SupportElementState : public SupportElementStateBits
|
||||||
uint32_t missing_roof_layers;
|
uint32_t missing_roof_layers;
|
||||||
|
|
||||||
// called by increase_single_area() and increaseAreas()
|
// called by increase_single_area() and increaseAreas()
|
||||||
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState& src)
|
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState &src)
|
||||||
{
|
{
|
||||||
SupportElementState dst{ src };
|
SupportElementState dst{ src };
|
||||||
++dst.distance_to_top;
|
++ dst.distance_to_top;
|
||||||
--dst.layer_idx;
|
-- dst.layer_idx;
|
||||||
// set to invalid as we are a new node on a new layer
|
// set to invalid as we are a new node on a new layer
|
||||||
dst.result_on_layer_reset();
|
dst.result_on_layer_reset();
|
||||||
dst.skip_ovalisation = false;
|
dst.skip_ovalisation = false;
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; }
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
|
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
|
||||||
|
@ -279,8 +278,6 @@ struct SupportElement
|
||||||
Polygons influence_area;
|
Polygons influence_area;
|
||||||
};
|
};
|
||||||
|
|
||||||
void tree_supports_show_error(std::string_view message, bool critical);
|
|
||||||
|
|
||||||
using SupportElements = std::deque<SupportElement>;
|
using SupportElements = std::deque<SupportElement>;
|
||||||
|
|
||||||
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
|
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
|
||||||
|
@ -293,39 +290,27 @@ using SupportElements = std::deque<SupportElement>;
|
||||||
return support_element_collision_radius(settings, elem.state);
|
return support_element_collision_radius(settings, elem.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_layer_pathing(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
|
|
||||||
|
|
||||||
void create_nodes_from_area(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
|
|
||||||
|
|
||||||
void organic_smooth_branches_avoid_collisions(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<std::pair<SupportElement*, int>>& elements_with_link_down, const std::vector<size_t>& linear_data_layers, std::function<void()> throw_on_cancel);
|
|
||||||
|
|
||||||
indexed_triangle_set draw_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
|
|
||||||
|
|
||||||
void slice_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, const indexed_triangle_set& cummulative_mesh, SupportGeneratorLayersPtr& bottom_contacts, SupportGeneratorLayersPtr& top_contacts, SupportGeneratorLayersPtr& intermediate_layers, SupportGeneratorLayerStorage& layer_storage, std::function<void()> throw_on_cancel);
|
|
||||||
|
|
||||||
void generate_initial_areas(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, InterfacePlacer& interface_placer, std::function<void()> throw_on_cancel);
|
|
||||||
|
|
||||||
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
|
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
|
||||||
void organic_draw_branches(
|
void organic_draw_branches(
|
||||||
PrintObject& print_object,
|
PrintObject &print_object,
|
||||||
TreeModelVolumes& volumes,
|
TreeModelVolumes &volumes,
|
||||||
const TreeSupportSettings& config,
|
const TreeSupportSettings &config,
|
||||||
std::vector<SupportElements>& move_bounds,
|
std::vector<SupportElements> &move_bounds,
|
||||||
|
|
||||||
// I/O:
|
// I/O:
|
||||||
SupportGeneratorLayersPtr& bottom_contacts,
|
SupportGeneratorLayersPtr &bottom_contacts,
|
||||||
SupportGeneratorLayersPtr& top_contacts,
|
SupportGeneratorLayersPtr &top_contacts,
|
||||||
InterfacePlacer& interface_placer,
|
InterfacePlacer &interface_placer,
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
SupportGeneratorLayersPtr& intermediate_layers,
|
SupportGeneratorLayersPtr &intermediate_layers,
|
||||||
SupportGeneratorLayerStorage& layer_storage,
|
SupportGeneratorLayerStorage &layer_storage,
|
||||||
|
|
||||||
std::function<void()> throw_on_cancel);
|
std::function<void()> throw_on_cancel);
|
||||||
|
|
||||||
} // namespace TreeSupport3D
|
} // namespace TreeSupport3D
|
||||||
|
|
||||||
void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_support, std::function<void()> throw_on_cancel = []{});
|
void generate_tree_support_3D(PrintObject &print_object, std::function<void()> throw_on_cancel = []{});
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
|
|
@ -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 ®ion = 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
|
|
|
@ -15,12 +15,10 @@
|
||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
using namespace Slic3r::FFFSupport;
|
|
||||||
|
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace FFFTreeSupport
|
namespace TreeSupport3D
|
||||||
{
|
{
|
||||||
|
|
||||||
using LayerIndex = int;
|
using LayerIndex = int;
|
||||||
|
@ -36,7 +34,92 @@ enum class InterfacePreference
|
||||||
|
|
||||||
struct TreeSupportMeshGroupSettings {
|
struct TreeSupportMeshGroupSettings {
|
||||||
TreeSupportMeshGroupSettings() = default;
|
TreeSupportMeshGroupSettings() = default;
|
||||||
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object);
|
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object)
|
||||||
|
{
|
||||||
|
const PrintConfig &print_config = print_object.print()->config();
|
||||||
|
const PrintObjectConfig &config = print_object.config();
|
||||||
|
const SlicingParameters &slicing_params = print_object.slicing_parameters();
|
||||||
|
// const std::vector<unsigned int> printing_extruders = print_object.object_extruders();
|
||||||
|
|
||||||
|
// Support must be enabled and set to Tree style.
|
||||||
|
assert(config.enable_support || config.enforce_support_layers > 0);
|
||||||
|
assert(is_tree(config.support_type));
|
||||||
|
|
||||||
|
// Calculate maximum external perimeter width over all printing regions, taking into account the default layer height.
|
||||||
|
coordf_t external_perimeter_width = 0.;
|
||||||
|
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
|
||||||
|
const PrintRegion ®ion = print_object.printing_region(region_id);
|
||||||
|
external_perimeter_width = std::max<coordf_t>(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width());
|
||||||
|
}
|
||||||
|
|
||||||
|
this->layer_height = scaled<coord_t>(config.layer_height.value);
|
||||||
|
this->resolution = scaled<coord_t>(print_config.resolution.value);
|
||||||
|
// Arache feature
|
||||||
|
this->min_feature_size = scaled<coord_t>(config.min_feature_size.value);
|
||||||
|
// +1 makes the threshold inclusive
|
||||||
|
this->support_angle = 0.5 * M_PI - std::clamp<double>((config.support_threshold_angle + 1) * M_PI / 180., 0., 0.5 * M_PI);
|
||||||
|
this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width();
|
||||||
|
this->support_roof_line_width = support_material_interface_flow(&print_object, config.layer_height).scaled_width();
|
||||||
|
//FIXME add it to SlicingParameters and reuse in both tree and normal supports?
|
||||||
|
this->support_bottom_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value != 0;
|
||||||
|
this->support_bottom_height = this->support_bottom_enable ?
|
||||||
|
(config.support_interface_bottom_layers.value > 0 ?
|
||||||
|
config.support_interface_bottom_layers.value :
|
||||||
|
config.support_interface_top_layers.value) * this->layer_height :
|
||||||
|
0;
|
||||||
|
this->support_material_buildplate_only = config.support_on_build_plate_only;
|
||||||
|
this->support_xy_distance = scaled<coord_t>(config.support_object_xy_distance.value);
|
||||||
|
// Separation of interfaces, it is likely smaller than support_xy_distance.
|
||||||
|
this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled<coord_t>(0.5 * external_perimeter_width));
|
||||||
|
this->support_top_distance = scaled<coord_t>(slicing_params.gap_support_object);
|
||||||
|
this->support_bottom_distance = scaled<coord_t>(slicing_params.gap_object_support);
|
||||||
|
// this->support_interface_skip_height =
|
||||||
|
// this->support_infill_angles =
|
||||||
|
this->support_roof_enable = config.support_interface_top_layers.value > 0;
|
||||||
|
this->support_roof_layers = this->support_roof_enable ? config.support_interface_top_layers.value : 0;
|
||||||
|
this->support_floor_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value > 0;
|
||||||
|
this->support_floor_layers = this->support_floor_enable ? config.support_interface_bottom_layers.value : 0;
|
||||||
|
// this->minimum_roof_area =
|
||||||
|
// this->support_roof_angles =
|
||||||
|
this->support_roof_pattern = config.support_interface_pattern;
|
||||||
|
this->support_pattern = config.support_base_pattern;
|
||||||
|
this->support_line_spacing = scaled<coord_t>(config.support_base_pattern_spacing.value);
|
||||||
|
// this->support_bottom_offset =
|
||||||
|
// this->support_wall_count = config.support_material_with_sheath ? 1 : 0;
|
||||||
|
this->support_wall_count = 1;
|
||||||
|
this->support_roof_line_distance = scaled<coord_t>(config.support_interface_spacing.value) + this->support_roof_line_width;
|
||||||
|
// this->minimum_support_area =
|
||||||
|
// this->minimum_bottom_area =
|
||||||
|
// this->support_offset =
|
||||||
|
this->support_tree_branch_distance = scaled<coord_t>(config.tree_support_branch_distance_organic.value);
|
||||||
|
this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle_organic * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
|
||||||
|
this->support_tree_angle_slow = std::clamp<double>(config.tree_support_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
|
||||||
|
this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter_organic.value);
|
||||||
|
this->support_tree_branch_diameter_angle = std::clamp<double>(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
|
||||||
|
this->support_tree_top_rate = config.tree_support_top_rate.value; // percent
|
||||||
|
// this->support_tree_tip_diameter = this->support_line_width;
|
||||||
|
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.tree_support_tip_diameter.value), (coord_t)0, this->support_tree_branch_diameter);
|
||||||
|
|
||||||
|
std::cout << "\n---------------\n"
|
||||||
|
<< "layer_height: " << layer_height << "\nresolution: " << resolution << "\nmin_feature_size: " << min_feature_size
|
||||||
|
<< "\nsupport_angle: " << support_angle << "\nconfig.support_threshold_angle: " << config.support_threshold_angle << "\nsupport_line_width: " << support_line_width
|
||||||
|
<< "\nsupport_roof_line_width: " << support_roof_line_width << "\nsupport_bottom_enable: " << support_bottom_enable
|
||||||
|
<< "\nsupport_bottom_height: " << support_bottom_height
|
||||||
|
<< "\nsupport_material_buildplate_only: " << support_material_buildplate_only
|
||||||
|
<< "\nsupport_xy_distance: " << support_xy_distance << "\nsupport_xy_distance_overhang: " << support_xy_distance_overhang
|
||||||
|
<< "\nsupport_top_distance: " << support_top_distance << "\nsupport_bottom_distance: " << support_bottom_distance
|
||||||
|
<< "\nsupport_roof_enable: " << support_roof_enable << "\nsupport_roof_layers: " << support_roof_layers
|
||||||
|
<< "\nsupport_floor_enable: " << support_floor_enable << "\nsupport_floor_layers: " << support_floor_layers
|
||||||
|
<< "\nsupport_roof_pattern: " << support_roof_pattern << "\nsupport_pattern: " << support_pattern
|
||||||
|
<< "\nsupport_line_spacing: " << support_line_spacing << "\nsupport_wall_count: " << support_wall_count
|
||||||
|
<< "\nsupport_roof_line_distance: " << support_roof_line_distance
|
||||||
|
<< "\nsupport_tree_branch_distance: " << support_tree_branch_distance
|
||||||
|
<< "\nsupport_tree_angle_slow: " << support_tree_angle_slow
|
||||||
|
<< "\nsupport_tree_branch_diameter: " << support_tree_branch_diameter
|
||||||
|
<< "\nsupport_tree_branch_diameter_angle: " << support_tree_branch_diameter_angle
|
||||||
|
<< "\nsupport_tree_top_rate: " << support_tree_top_rate << "\nsupport_tree_tip_diameter: " << support_tree_tip_diameter
|
||||||
|
<< "\n---------------\n";
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
/* Print parameters, not support specific: */
|
/* Print parameters, not support specific: */
|
||||||
|
@ -209,7 +292,93 @@ struct TreeSupportSettings
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class.
|
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class.
|
||||||
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params);
|
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params)
|
||||||
|
: support_line_width(mesh_group_settings.support_line_width),
|
||||||
|
layer_height(mesh_group_settings.layer_height),
|
||||||
|
branch_radius(mesh_group_settings.support_tree_branch_diameter / 2),
|
||||||
|
min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance
|
||||||
|
maximum_move_distance((mesh_group_settings.support_tree_angle < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle) * layer_height) : std::numeric_limits<coord_t>::max()),
|
||||||
|
maximum_move_distance_slow((mesh_group_settings.support_tree_angle_slow < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
|
||||||
|
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
|
||||||
|
// Ensure lines always stack nicely even if layer height is large.
|
||||||
|
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)),
|
||||||
|
branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height),
|
||||||
|
max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2),
|
||||||
|
min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)),
|
||||||
|
increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2),
|
||||||
|
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / branch_radius_increase_per_layer),
|
||||||
|
support_rests_on_model(! mesh_group_settings.support_material_buildplate_only),
|
||||||
|
xy_distance(mesh_group_settings.support_xy_distance),
|
||||||
|
xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)),
|
||||||
|
bp_radius(mesh_group_settings.support_tree_bp_diameter / 2),
|
||||||
|
// Increase by half a line overlap, but not faster than 40 degrees angle (0 degrees means zero increase in radius).
|
||||||
|
bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
|
||||||
|
z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))),
|
||||||
|
z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))),
|
||||||
|
// support_infill_angles(mesh_group_settings.support_infill_angles),
|
||||||
|
support_roof_angles(mesh_group_settings.support_roof_angles),
|
||||||
|
roof_pattern(mesh_group_settings.support_roof_pattern),
|
||||||
|
support_pattern(mesh_group_settings.support_pattern),
|
||||||
|
support_roof_line_width(mesh_group_settings.support_roof_line_width),
|
||||||
|
support_line_spacing(mesh_group_settings.support_line_spacing),
|
||||||
|
support_bottom_offset(mesh_group_settings.support_bottom_offset),
|
||||||
|
support_wall_count(mesh_group_settings.support_wall_count),
|
||||||
|
resolution(mesh_group_settings.resolution),
|
||||||
|
support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference.
|
||||||
|
settings(mesh_group_settings),
|
||||||
|
min_feature_size(mesh_group_settings.min_feature_size)
|
||||||
|
{
|
||||||
|
// At least one tip layer must be defined.
|
||||||
|
assert(tip_layers > 0);
|
||||||
|
|
||||||
|
layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer;
|
||||||
|
|
||||||
|
if (TreeSupportSettings::soluble) {
|
||||||
|
// safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely
|
||||||
|
// When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size
|
||||||
|
// This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance.
|
||||||
|
xy_min_distance = std::max(xy_min_distance, scaled<coord_t>(0.1));
|
||||||
|
xy_distance = std::max(xy_distance, xy_min_distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } };
|
||||||
|
// interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority"));
|
||||||
|
//FIXME this was the default
|
||||||
|
// interface_preference = InterfacePreference::SupportLinesOverwriteInterface;
|
||||||
|
//interface_preference = InterfacePreference::SupportAreaOverwritesInterface;
|
||||||
|
interface_preference = InterfacePreference::InterfaceAreaOverwritesSupport;
|
||||||
|
|
||||||
|
if (slicing_params.raft_layers() > 0) {
|
||||||
|
// Fill in raft_layers with the heights of the layers below the first object layer.
|
||||||
|
// First layer
|
||||||
|
double z = slicing_params.first_print_layer_height;
|
||||||
|
this->raft_layers.emplace_back(z);
|
||||||
|
// Raft base layers
|
||||||
|
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
|
||||||
|
z += slicing_params.base_raft_layer_height;
|
||||||
|
this->raft_layers.emplace_back(z);
|
||||||
|
}
|
||||||
|
// Raft interface layers
|
||||||
|
for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) {
|
||||||
|
z += slicing_params.interface_raft_layer_height;
|
||||||
|
this->raft_layers.emplace_back(z);
|
||||||
|
}
|
||||||
|
// Raft contact layer
|
||||||
|
if (slicing_params.raft_layers() > 1) {
|
||||||
|
z = slicing_params.raft_contact_top_z;
|
||||||
|
this->raft_layers.emplace_back(z);
|
||||||
|
}
|
||||||
|
if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) {
|
||||||
|
// Layers between the raft contacts and bottom of the object.
|
||||||
|
auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height));
|
||||||
|
double step = dist_to_go / nsteps;
|
||||||
|
for (int i = 0; i < nsteps; ++ i) {
|
||||||
|
z += step;
|
||||||
|
this->raft_layers.emplace_back(z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// some static variables dependent on other meshes that are not currently processed.
|
// some static variables dependent on other meshes that are not currently processed.
|
||||||
// Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy.
|
// Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy.
|
||||||
|
@ -450,7 +619,20 @@ static constexpr const bool polygons_strictly_simple = false;
|
||||||
|
|
||||||
inline double tiny_area_threshold() { return sqr(scaled<double>(0.001)); }
|
inline double tiny_area_threshold() { return sqr(scaled<double>(0.001)); }
|
||||||
|
|
||||||
void tree_supports_show_error(std::string_view message, bool critical);
|
void tree_supports_show_error(std::string_view message, bool critical)
|
||||||
|
{ // todo Remove! ONLY FOR PUBLIC BETA!!
|
||||||
|
printf("Error: %s, critical: %d\n", message.data(), int(critical));
|
||||||
|
#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
|
||||||
|
static bool g_showed_critical_error = false;
|
||||||
|
static bool g_showed_performance_warning = false;
|
||||||
|
auto bugtype = std::string(critical ? " This is a critical bug. It may cause missing or malformed branches.\n" : "This bug should only decrease performance.\n");
|
||||||
|
bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning);
|
||||||
|
(critical ? g_showed_critical_error : g_showed_performance_warning) = true;
|
||||||
|
if (show)
|
||||||
|
MessageBoxA(nullptr, std::string("TreeSupport_2 MOD detected an error while generating the tree support.\nPlease report this back to me with profile and model.\nRevision 5.0\n" + std::string(message) + "\n" + bugtype).c_str(),
|
||||||
|
"Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
|
||||||
|
#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
|
||||||
|
}
|
||||||
|
|
||||||
inline double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
|
inline double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
|
||||||
{
|
{
|
||||||
|
@ -584,7 +766,7 @@ private:
|
||||||
std::mutex m_mutex_layer_storage;
|
std::mutex m_mutex_layer_storage;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FFFTreeSupport
|
} // namespace TreeSupport3D
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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
|
@ -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 */
|
|
Loading…
Add table
Add a link
Reference in a new issue