ENH: add rectilinear interface pattern for organic support

1. add rectilinear interface pattern for organic support
    jira: STUDIO-7181
2. add tree support optgroup

Change-Id: I94882bc34a61c6adc06b8ecbc9f2323f9b039aac
(cherry picked from commit a8142ab3f37e0bd140a31a7e635b8475f471d7e3)
This commit is contained in:
Arthur 2024-05-28 20:11:45 +08:00 committed by Lane.Wei
parent c80a3fc5d1
commit 69cf816b94
15 changed files with 2275 additions and 2171 deletions

View File

@ -281,6 +281,8 @@ set(lisbslic3r_sources
SlicesToTriangleMesh.cpp SlicesToTriangleMesh.cpp
SlicingAdaptive.cpp SlicingAdaptive.cpp
SlicingAdaptive.hpp SlicingAdaptive.hpp
Support/SupportCommon.cpp
Support/SupportCommon.hpp
Support/SupportMaterial.cpp Support/SupportMaterial.cpp
Support/SupportMaterial.hpp Support/SupportMaterial.hpp
Support/TreeSupport.hpp Support/TreeSupport.hpp
@ -290,6 +292,7 @@ set(lisbslic3r_sources
Support/TreeModelVolumes.hpp Support/TreeModelVolumes.hpp
Support/TreeModelVolumes.cpp Support/TreeModelVolumes.cpp
Support/TreeSupportCommon.hpp Support/TreeSupportCommon.hpp
Support/SupportParameters.hpp
MinimumSpanningTree.hpp MinimumSpanningTree.hpp
MinimumSpanningTree.cpp MinimumSpanningTree.cpp
Surface.cpp Surface.cpp

View File

@ -269,8 +269,8 @@ bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
// Offset CCW contours outside, CW contours (holes) inside. // Offset CCW contours outside, CW contours (holes) inside.
// Don't calculate union of the output paths. // Don't calculate union of the output paths.
template<typename PathsProvider, ClipperLib::EndType endType = ClipperLib::etClosedPolygon> template<typename PathsProvider>
static ClipperLib::Paths raw_offset(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) static ClipperLib::Paths raw_offset(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType endType = ClipperLib::etClosedPolygon)
{ {
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
ClipperLib::Paths out; ClipperLib::Paths out;
@ -280,7 +280,7 @@ static ClipperLib::Paths raw_offset(PathsProvider &&paths, float offset, Clipper
co.ArcTolerance = miterLimit; co.ArcTolerance = miterLimit;
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(offset * ClipperOffsetShortestEdgeFactor)); co.ShortestEdgeLength = std::abs(offset * ClipperOffsetShortestEdgeFactor);
for (const ClipperLib::Path &path : paths) { for (const ClipperLib::Path &path : paths) {
co.Clear(); co.Clear();
// Execute reorients the contours so that the outer most contour has a positive area. Thus the output // Execute reorients the contours so that the outer most contour has a positive area. Thus the output
@ -356,11 +356,11 @@ ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, bool
return PolyTreeToExPolygons(clipper_union<ClipperLib::PolyTree>(input, do_union ? ClipperLib::pftNonZero : ClipperLib::pftEvenOdd)); return PolyTreeToExPolygons(clipper_union<ClipperLib::PolyTree>(input, do_union ? ClipperLib::pftNonZero : ClipperLib::pftEvenOdd));
} }
template<typename PathsProvider, ClipperLib::EndType endType = ClipperLib::etClosedPolygon> template<typename PathsProvider>
static ClipperLib::Paths raw_offset_polyline(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) static ClipperLib::Paths raw_offset_polyline(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType end_type = ClipperLib::etOpenButt)
{ {
assert(offset > 0); assert(offset > 0);
return raw_offset<PathsProvider, ClipperLib::etOpenButt>(std::forward<PathsProvider>(paths), offset, joinType, miterLimit); return raw_offset<PathsProvider>(std::forward<PathsProvider>(paths), offset, joinType, miterLimit, end_type);
} }
template<class TResult, typename PathsProvider> template<class TResult, typename PathsProvider>
@ -415,10 +415,10 @@ Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, Cli
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType, double miterLimit) Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType, double miterLimit)
{ return PolyTreeToExPolygons(offset_paths<ClipperLib::PolyTree>(ClipperUtils::PolygonsProvider(polygons), delta, joinType, miterLimit)); } { return PolyTreeToExPolygons(offset_paths<ClipperLib::PolyTree>(ClipperUtils::PolygonsProvider(polygons), delta, joinType, miterLimit)); }
Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType, double miterLimit) Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType end_type)
{ assert(delta > 0); return to_polygons(clipper_union<ClipperLib::Paths>(raw_offset_polyline(ClipperUtils::SinglePathProvider(polyline.points), delta, joinType, miterLimit))); } { assert(delta > 0); return to_polygons(clipper_union<ClipperLib::Paths>(raw_offset_polyline(ClipperUtils::SinglePathProvider(polyline.points), delta, joinType, miterLimit, end_type))); }
Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType, double miterLimit) Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType end_type)
{ assert(delta > 0); return to_polygons(clipper_union<ClipperLib::Paths>(raw_offset_polyline(ClipperUtils::PolylinesProvider(polylines), delta, joinType, miterLimit))); } { assert(delta > 0); return to_polygons(clipper_union<ClipperLib::Paths>(raw_offset_polyline(ClipperUtils::PolylinesProvider(polylines), delta, joinType, miterLimit, end_type))); }
// returns number of expolygons collected (0 or 1). // returns number of expolygons collected (0 or 1).
static int offset_expolygon_inner(const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::Paths &out) static int offset_expolygon_inner(const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::Paths &out)

View File

@ -29,6 +29,7 @@ class BoundingBox;
static constexpr const float ClipperSafetyOffset = 10.f; static constexpr const float ClipperSafetyOffset = 10.f;
static constexpr const Slic3r::ClipperLib::JoinType DefaultJoinType = Slic3r::ClipperLib::jtMiter; static constexpr const Slic3r::ClipperLib::JoinType DefaultJoinType = Slic3r::ClipperLib::jtMiter;
static constexpr const Slic3r::ClipperLib::EndType DefaultEndType = Slic3r::ClipperLib::etOpenButt;
//FIXME evaluate the default miter limit. 3 seems to be extreme, Cura uses 1.2. //FIXME evaluate the default miter limit. 3 seems to be extreme, Cura uses 1.2.
// Mitter Limit 3 is useful for perimeter generator, where sharp corners are extruded without needing a gap fill. // Mitter Limit 3 is useful for perimeter generator, where sharp corners are extruded without needing a gap fill.
// However such a high limit causes issues with large positive or negative offsets, where a sharp corner // However such a high limit causes issues with large positive or negative offsets, where a sharp corner
@ -342,8 +343,8 @@ Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, Clipp
// offset Polylines // offset Polylines
// Wherever applicable, please use the expand() / shrink() variants instead, they convey their purpose better. // Wherever applicable, please use the expand() / shrink() variants instead, they convey their purpose better.
// Input polygons for negative offset shall be "normalized": There must be no overlap / intersections between the input polygons. // Input polygons for negative offset shall be "normalized": There must be no overlap / intersections between the input polygons.
Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit); Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit); Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit); Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit); Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit); Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);

View File

@ -50,7 +50,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipOctagramSpiral: return new FillOctagramSpiral(); case ipOctagramSpiral: return new FillOctagramSpiral();
case ipAdaptiveCubic: return new FillAdaptive::Filler(); case ipAdaptiveCubic: return new FillAdaptive::Filler();
case ipSupportCubic: return new FillAdaptive::Filler(); case ipSupportCubic: return new FillAdaptive::Filler();
case ipSupportBase: return new FillSupportBase(); case ipSupportBase: return new FillSupportBase(); // simply line fill
case ipLightning: return new FillLightning::Filler(); case ipLightning: return new FillLightning::Filler();
// BBS: for internal solid infill only // BBS: for internal solid infill only
case ipConcentricInternal: return new FillConcentricInternal(); case ipConcentricInternal: return new FillConcentricInternal();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,150 @@
///|/ Copyright (c) Prusa Research 2023 Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_SupportCommon_hpp_
#define slic3r_SupportCommon_hpp_
#include "../Layer.hpp"
#include "../Polygon.hpp"
#include "../Print.hpp"
#include "SupportLayer.hpp"
#include "SupportParameters.hpp"
namespace Slic3r {
class PrintObject;
class SupportLayer;
// 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.
// For Organic supports, merge top_interface_layers & top_base_interface_layers with the interfaces
// produced by this function.
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
const PrintObjectConfig &config,
const SupportParameters &support_params,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
// Input / output, will be merged with output
SupportGeneratorLayersPtr &top_interface_layers,
SupportGeneratorLayersPtr &top_base_interface_layers,
// Input, will be trimmed with the newly created interface layers.
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage);
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
SupportGeneratorLayersPtr generate_raft_base(
const PrintObject &object,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers,
const SupportGeneratorLayersPtr &base_layers,
SupportGeneratorLayerStorage &layer_storage);
void fill_expolygons_with_sheath_generate_paths(ExtrusionEntitiesPtr& dst, const Polygons& polygons, Fill* filler, float density, ExtrusionRole role, const Flow& flow, bool with_sheath, bool no_sort);
// returns sorted layers
SupportGeneratorLayersPtr generate_support_layers(
PrintObject &object,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
// Produce the support G-code.
// Used by both classic and tree supports.
void generate_support_toolpaths(
SupportLayerPtrs &support_layers,
const PrintObjectConfig &config,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
// FN_HIGHER_EQUAL: the provided object pointer has a Z value >= of an internal threshold.
// Find the first item with Z value >= of an internal threshold of fn_higher_equal.
// If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size()
// If the initial idx is size_t(-1), then use binary search.
// Otherwise search linearly upwards.
template<typename IteratorType, typename IndexType, typename FN_HIGHER_EQUAL>
IndexType idx_higher_or_equal(IteratorType begin, IteratorType end, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
{
auto size = int(end - begin);
if (size == 0) {
idx = 0;
} else if (idx == IndexType(-1)) {
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0;
int idx_high = std::max(0, size - 1);
while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2;
if (fn_higher_equal(begin[idx_mid]))
idx_high = idx_mid;
else
idx_low = idx_mid;
}
idx = fn_higher_equal(begin[idx_low]) ? idx_low :
(fn_higher_equal(begin[idx_high]) ? idx_high : size);
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
while (int(idx) < size && ! fn_higher_equal(begin[idx]))
++ idx;
}
return idx;
}
template<typename T, typename IndexType, typename FN_HIGHER_EQUAL>
IndexType idx_higher_or_equal(const std::vector<T>& vec, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
{
return idx_higher_or_equal(vec.begin(), vec.end(), idx, fn_higher_equal);
}
// FN_LOWER_EQUAL: the provided object pointer has a Z value <= of an internal threshold.
// Find the first item with Z value <= of an internal threshold of fn_lower_equal.
// If no vec item with Z value <= of an internal threshold of fn_lower_equal is found, return -1.
// If the initial idx is < -1, then use binary search.
// Otherwise search linearly downwards.
template<typename IT, typename FN_LOWER_EQUAL>
int idx_lower_or_equal(IT begin, IT end, int idx, FN_LOWER_EQUAL fn_lower_equal)
{
auto size = int(end - begin);
if (size == 0) {
idx = -1;
} else if (idx < -1) {
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0;
int idx_high = std::max(0, size - 1);
while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2;
if (fn_lower_equal(begin[idx_mid]))
idx_low = idx_mid;
else
idx_high = idx_mid;
}
idx = fn_lower_equal(begin[idx_high]) ? idx_high :
(fn_lower_equal(begin[idx_low ]) ? idx_low : -1);
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
while (idx >= 0 && ! fn_lower_equal(begin[idx]))
-- idx;
}
return idx;
}
template<typename T, typename FN_LOWER_EQUAL>
int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL fn_lower_equal)
{
return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal);
}
} // namespace Slic3r
#endif /* slic3r_SupportCommon_hpp_ */

File diff suppressed because it is too large Load Diff

View File

@ -19,14 +19,6 @@ inline double layer_z(const SlicingParameters& slicing_params, const size_t laye
{ {
return slicing_params.object_print_z_min + slicing_params.first_object_layer_height + layer_idx * slicing_params.layer_height; return slicing_params.object_print_z_min + slicing_params.first_object_layer_height + layer_idx * slicing_params.layer_height;
} }
inline LayerIndex layer_idx_ceil(const SlicingParameters& slicing_params, const double z)
{
return LayerIndex(ceil((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
}
inline LayerIndex layer_idx_floor(const SlicingParameters& slicing_params, const double z)
{
return LayerIndex(floor((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
}
inline SupportGeneratorLayer& layer_initialize( inline SupportGeneratorLayer& layer_initialize(
SupportGeneratorLayer& layer_new, SupportGeneratorLayer& layer_new,
@ -53,67 +45,6 @@ inline SupportGeneratorLayer& layer_allocate(
return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx); return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx);
} }
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
SupportGeneratorLayersPtr generate_raft_base(
const PrintObject &object,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers,
const SupportGeneratorLayersPtr &base_layers,
SupportGeneratorLayerStorage &layer_storage);
// returns sorted layers
SupportGeneratorLayersPtr generate_support_layers(
PrintObject &object,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
// 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<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
const PrintObjectConfig& config,
const SupportParameters& support_params,
const SupportGeneratorLayersPtr& bottom_contacts,
const SupportGeneratorLayersPtr& top_contacts,
// Input / output, will be merged with output. Only provided for Organic supports.
SupportGeneratorLayersPtr& top_interface_layers,
SupportGeneratorLayersPtr& top_base_interface_layers,
SupportGeneratorLayersPtr& intermediate_layers,
SupportGeneratorLayerStorage& layer_storage);
// Produce the support G-code.
// Used by both classic and tree supports.
void generate_support_toolpaths(
PrintObject &object,
SupportLayerPtrs &support_layers,
const PrintObjectConfig &config,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
void fill_expolygons_with_sheath_generate_paths(
ExtrusionEntitiesPtr& dst,
const Polygons& polygons,
Fill* filler,
float density,
ExtrusionRole role,
const Flow& flow,
bool with_sheath,
bool no_sort);
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers); 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); void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer);

View File

@ -10,6 +10,7 @@
namespace Slic3r { namespace Slic3r {
struct SupportParameters { struct SupportParameters {
SupportParameters() = default;
SupportParameters(const PrintObject& object) SupportParameters(const PrintObject& object)
{ {
const PrintConfig& print_config = object.print()->config(); const PrintConfig& print_config = object.print()->config();
@ -43,6 +44,7 @@ struct SupportParameters {
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); 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_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->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. // 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); this->support_layer_height_min = scaled<coord_t>(0.01);
@ -89,6 +91,8 @@ struct SupportParameters {
this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.)); this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.));
this->interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing(); this->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() / this->interface_spacing); this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / this->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);
this->support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing(); this->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() / this->support_spacing); this->support_density = std::min(1., this->support_material_flow.spacing() / this->support_spacing);
if (object_config.support_interface_top_layers.value == 0) { if (object_config.support_interface_top_layers.value == 0) {
@ -98,11 +102,12 @@ struct SupportParameters {
} }
SupportMaterialPattern support_pattern = object_config.support_base_pattern; SupportMaterialPattern support_pattern = object_config.support_base_pattern;
this->with_sheath = /*is_tree(object_config.support_type) &&*/ object_config.tree_support_wall_count > 0; this->with_sheath = object_config.tree_support_wall_count > 0;
this->base_fill_pattern = this->base_fill_pattern =
support_pattern == smpHoneycomb ? ipHoneycomb : support_pattern == smpHoneycomb ? ipHoneycomb :
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
this->interface_fill_pattern = (this->interface_density > 0.95 ? 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;
if (object_config.support_interface_pattern == smipGrid) if (object_config.support_interface_pattern == smipGrid)
this->contact_fill_pattern = ipGrid; this->contact_fill_pattern = ipGrid;
else if (object_config.support_interface_pattern == smipRectilinearInterlaced) else if (object_config.support_interface_pattern == smipRectilinearInterlaced)
@ -113,6 +118,40 @@ struct SupportParameters {
object_config.support_interface_pattern == smipConcentric ? object_config.support_interface_pattern == smipConcentric ?
ipConcentric : ipConcentric :
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
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);
}
double tree_support_branch_diameter_double_wall = 3.0; // in organic support, Branches with area larger than the area of a circle of this diameter will be printed with double walls for stability
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(tree_support_branch_diameter_double_wall)) * M_PI;
} }
// Both top / bottom contacts and interfaces are soluble. // Both top / bottom contacts and interfaces are soluble.
bool soluble_interface; bool soluble_interface;
@ -142,6 +181,8 @@ struct SupportParameters {
Flow support_material_flow; Flow support_material_flow;
Flow support_material_interface_flow; Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow; Flow support_material_bottom_interface_flow;
// Flow at raft inteface & contact layers.
Flow raft_interface_flow;
coordf_t support_extrusion_width; coordf_t support_extrusion_width;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions; bool can_merge_support_regions;
@ -157,13 +198,28 @@ struct SupportParameters {
coordf_t interface_spacing; coordf_t interface_spacing;
coordf_t support_expansion=0; coordf_t support_expansion=0;
coordf_t interface_density; coordf_t interface_density;
// Density of the raft interface and contact layers.
coordf_t raft_interface_density;
coordf_t support_spacing; coordf_t support_spacing;
coordf_t support_density; coordf_t support_density;
InfillPattern base_fill_pattern; InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern; InfillPattern interface_fill_pattern;
// Pattern of the raft interface and contact layers.
InfillPattern raft_interface_fill_pattern;
InfillPattern contact_fill_pattern; InfillPattern contact_fill_pattern;
bool with_sheath; bool with_sheath;
// Branches of organic supports with area larger than this threshold will be extruded with double lines.
double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(3.0)) * M_PI;;
float raft_angle_1st_layer;
float raft_angle_base;
float raft_angle_interface;
// Produce a raft interface angle for a given SupportLayer::interface_id()
float raft_interface_angle(size_t interface_id) const
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
bool independent_layer_height = false; bool independent_layer_height = false;
const double thresh_big_overhang = Slic3r::sqr(scale_(10)); const double thresh_big_overhang = Slic3r::sqr(scale_(10));
}; };

View File

@ -1,28 +1,29 @@
#include <math.h> #include <math.h>
#include <chrono> #include <chrono>
#include <math.h>
#include "MinimumSpanningTree.hpp"
#include "TreeSupport.hpp"
#include "Print.hpp"
#include "Layer.hpp"
#include "Fill/FillBase.hpp"
#include "Fill/FillConcentric.hpp"
#include "CurveAnalyzer.hpp"
#include "SVG.hpp"
#include "ShortestPath.hpp"
#include "I18N.hpp"
#include <libnest2d/backends/libslic3r/geometries.hpp>
#include <libnest2d/placers/nfpplacer.hpp>
#include "TreeModelVolumes.hpp"
#include "TreeSupport3D.hpp"
#include "SupportMaterial.hpp"
#include "Fill/FillBase.hpp"
#include "BuildVolume.hpp" #include "BuildVolume.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "CurveAnalyzer.hpp"
#include "Fill/FillBase.hpp"
#include "Fill/FillBase.hpp"
#include "Fill/FillConcentric.hpp"
#include "I18N.hpp"
#include "Layer.hpp"
#include "MinimumSpanningTree.hpp"
#include "Print.hpp"
#include "ShortestPath.hpp"
#include "SupportCommon.hpp"
#include "SVG.hpp"
#include "TreeSupportCommon.hpp"
#include "TreeSupport.hpp"
#include "TreeSupport3D.hpp"
#include <libnest2d/backends/libslic3r/geometries.hpp>
#include <libnest2d/placers/nfpplacer.hpp>
#include <tbb/concurrent_vector.h>
#include <tbb/concurrent_unordered_set.h>
#include <tbb/blocked_range.h> #include <tbb/blocked_range.h>
#include <tbb/concurrent_unordered_set.h>
#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h> #include <tbb/parallel_for_each.h>
@ -618,21 +619,9 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
SupportMaterialPattern support_pattern = m_object_config->support_base_pattern; SupportMaterialPattern support_pattern = m_object_config->support_base_pattern;
if (support_style == smsTreeHybrid && support_pattern == smpDefault) if (support_style == smsTreeHybrid && support_pattern == smpDefault)
support_pattern = smpRectilinear; support_pattern = smpRectilinear;
m_support_params.base_fill_pattern =
support_pattern == smpLightning ? ipLightning :
support_pattern == smpHoneycomb ? ipHoneycomb :
m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase;
m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); if(support_pattern == smpLightning)
if (m_object_config->support_interface_pattern == smipGrid) m_support_params.base_fill_pattern = ipLightning;
m_support_params.contact_fill_pattern = ipGrid;
else if (m_object_config->support_interface_pattern == smipRectilinearInterlaced)
m_support_params.contact_fill_pattern = ipRectilinear;
else
m_support_params.contact_fill_pattern = (m_object_config->support_interface_pattern == smipAuto && m_slicing_params.soluble_interface) ||
m_object_config->support_interface_pattern == smipConcentric ?
ipConcentric :
(m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase);
m_support_params.support_extrusion_width = m_object_config->support_line_width.value > 0 ? m_object_config->support_line_width : m_object_config->line_width; m_support_params.support_extrusion_width = m_object_config->support_line_width.value > 0 ? m_object_config->support_line_width : m_object_config->line_width;
// Check if set to zero, use default if so. // Check if set to zero, use default if so.
if (m_support_params.support_extrusion_width <= 0.0) { if (m_support_params.support_extrusion_width <= 0.0) {
@ -1657,8 +1646,6 @@ void deleteDirectoryContents(const std::filesystem::path& dir)
void TreeSupport::generate() void TreeSupport::generate()
{ {
auto t_start = std::chrono::high_resolution_clock::now();
if (support_style == smsTreeOrganic) { if (support_style == smsTreeOrganic) {
generate_tree_support_3D(*m_object, this, this->throw_on_cancel); generate_tree_support_3D(*m_object, this, this->throw_on_cancel);
return; return;
@ -1676,9 +1663,6 @@ void TreeSupport::generate()
m_ts_data = m_object->alloc_tree_support_preview_cache(); m_ts_data = m_object->alloc_tree_support_preview_cache();
m_ts_data->is_slim = is_slim; m_ts_data->is_slim = is_slim;
// Generate contact points of tree support
std::vector<std::vector<SupportNode*>> contact_nodes(m_object->layers().size());
#if USE_SUPPORT_3D #if USE_SUPPORT_3D
std::vector<TreeSupport3D::SupportElements> move_bounds(m_highest_overhang_layer + 1); std::vector<TreeSupport3D::SupportElements> move_bounds(m_highest_overhang_layer + 1);
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
@ -1713,22 +1697,22 @@ void TreeSupport::generate()
TreeSupport3D::generate_initial_areas(*m_object, *m_model_volumes.get(), tree_support_3d_config, overhangs, move_bounds, top_contacts, layer_storage, throw_on_cancel); TreeSupport3D::generate_initial_areas(*m_object, *m_model_volumes.get(), tree_support_3d_config, overhangs, move_bounds, top_contacts, layer_storage, throw_on_cancel);
} }
#endif #endif
generate_contact_points(contact_nodes); generate_contact_points();
profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES); profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES);
//Drop nodes to lower layers. //Drop nodes to lower layers.
profiler.stage_start(STAGE_DROP_DOWN_NODES); profiler.stage_start(STAGE_DROP_DOWN_NODES);
m_object->print()->set_status(60, _u8L("Generating support")); m_object->print()->set_status(60, _u8L("Generating support"));
drop_nodes(contact_nodes); drop_nodes();
profiler.stage_finish(STAGE_DROP_DOWN_NODES); profiler.stage_finish(STAGE_DROP_DOWN_NODES);
smooth_nodes(contact_nodes);// , tree_support_3d_config); smooth_nodes();// , tree_support_3d_config);
//Generate support areas. //Generate support areas.
profiler.stage_start(STAGE_DRAW_CIRCLES); profiler.stage_start(STAGE_DRAW_CIRCLES);
m_object->print()->set_status(65, _u8L("Generating support")); m_object->print()->set_status(65, _u8L("Generating support"));
draw_circles(contact_nodes); draw_circles();
profiler.stage_finish(STAGE_DRAW_CIRCLES); profiler.stage_finish(STAGE_DRAW_CIRCLES);
@ -1775,7 +1759,6 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_to
{ {
radius = mm_to_top;// this is a 45 degree tip radius = mm_to_top;// this is a 45 degree tip
} }
radius = std::max(radius, MIN_BRANCH_RADIUS); radius = std::max(radius, MIN_BRANCH_RADIUS);
radius = std::min(radius, MAX_BRANCH_RADIUS); radius = std::min(radius, MAX_BRANCH_RADIUS);
// if have interface layers, radius should be larger // if have interface layers, radius should be larger
@ -1943,7 +1926,7 @@ Polygons TreeSupport::get_trim_support_regions(
return polygons_trimming; return polygons_trimming;
} }
void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& contact_nodes) void TreeSupport::draw_circles()
{ {
const PrintObjectConfig &config = m_object->config(); const PrintObjectConfig &config = m_object->config();
const Print* print = m_object->print(); const Print* print = m_object->print();
@ -2045,11 +2028,11 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
} }
}; };
BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << curr_layer_nodes.size();
//Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas.
ts_layer->lslices.reserve(contact_nodes[layer_nr].size()); ts_layer->lslices.reserve(curr_layer_nodes.size());
ExPolygons area_poly; // the polygon node area which will be printed as normal support ExPolygons area_poly; // the polygon node area which will be printed as normal support
for (const SupportNode* p_node : contact_nodes[layer_nr]) for (const SupportNode* p_node : curr_layer_nodes)
{ {
if (print->canceled()) if (print->canceled())
break; break;
@ -2441,7 +2424,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
double SupportNode::diameter_angle_scale_factor; double SupportNode::diameter_angle_scale_factor;
void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nodes) void TreeSupport::drop_nodes()
{ {
const PrintObjectConfig &config = m_object->config(); const PrintObjectConfig &config = m_object->config();
// Use Minimum Spanning Tree to connect the points on each layer and move them while dropping them down. // Use Minimum Spanning Tree to connect the points on each layer and move them while dropping them down.
@ -2477,7 +2460,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
return move_dist; return move_dist;
}; };
m_ts_data->layer_heights = plan_layer_heights(contact_nodes); m_ts_data->layer_heights = plan_layer_heights();
std::vector<LayerHeightData> &layer_heights = m_ts_data->layer_heights; std::vector<LayerHeightData> &layer_heights = m_ts_data->layer_heights;
if (layer_heights.empty()) return; if (layer_heights.empty()) return;
@ -2540,7 +2523,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
coordf_t height_next = layer_heights[layer_nr_next].height; coordf_t height_next = layer_heights[layer_nr_next].height;
std::deque<std::pair<size_t, SupportNode*>> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. std::deque<std::pair<size_t, SupportNode*>> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches.
const Layer* ts_layer = m_object->get_support_layer(layer_nr);
m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating support"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating support"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str());
@ -2803,7 +2785,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
// 1. do not merge neighbors under 5mm // 1. do not merge neighbors under 5mm
// 2. Only merge node with single neighbor in distance between [max_move_distance, 10mm/layer_height] // 2. Only merge node with single neighbor in distance between [max_move_distance, 10mm/layer_height]
float dist2_to_first_neighbor = neighbours.empty() ? 0 : vsize2_with_unscale(neighbours[0] - node.position); float dist2_to_first_neighbor = neighbours.empty() ? 0 : vsize2_with_unscale(neighbours[0] - node.position);
if (ts_layer->print_z > DO_NOT_MOVER_UNDER_MM && if (node.print_z > DO_NOT_MOVER_UNDER_MM &&
(neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= max_move_distance2))) // Only nodes that aren't about to collapse. (neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= max_move_distance2))) // Only nodes that aren't about to collapse.
{ {
// Move towards the average position of all neighbours. // Move towards the average position of all neighbours.
@ -2819,7 +2801,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
coordf_t branch_bottom_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top + node.print_z, diameter_angle_scale_factor); coordf_t branch_bottom_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top + node.print_z, diameter_angle_scale_factor);
coordf_t neighbour_bottom_radius = calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top + neighbour_node->print_z, diameter_angle_scale_factor); coordf_t neighbour_bottom_radius = calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top + neighbour_node->print_z, diameter_angle_scale_factor);
double max_converge_distance = tan_angle * (ts_layer->print_z - DO_NOT_MOVER_UNDER_MM) + std::max(branch_bottom_radius, neighbour_bottom_radius); double max_converge_distance = tan_angle * (p_node->print_z - DO_NOT_MOVER_UNDER_MM) + std::max(branch_bottom_radius, neighbour_bottom_radius);
if (dist2_to_neighbor > max_converge_distance * max_converge_distance) continue; if (dist2_to_neighbor > max_converge_distance * max_converge_distance) continue;
if (is_line_cut_by_contour(node.position, neighbour)) continue; if (is_line_cut_by_contour(node.position, neighbour)) continue;
@ -2967,7 +2949,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
} }
void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode *>> &contact_nodes) void TreeSupport::smooth_nodes()
{ {
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
std::vector<SupportNode *> &curr_layer_nodes = contact_nodes[layer_nr]; std::vector<SupportNode *> &curr_layer_nodes = contact_nodes[layer_nr];
@ -3039,7 +3021,7 @@ void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode *>> &contact_
} }
#if USE_SUPPORT_3D #if USE_SUPPORT_3D
void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode*>>& contact_nodes, const TreeSupport3D::TreeSupportSettings& config) void TreeSupport::smooth_nodes(const TreeSupport3D::TreeSupportSettings& config)
{ {
const coordf_t branch_radius = m_object_config->tree_support_branch_diameter.value / 2; const coordf_t branch_radius = m_object_config->tree_support_branch_diameter.value / 2;
const coordf_t branch_radius_scaled = scale_(branch_radius); const coordf_t branch_radius_scaled = scale_(branch_radius);
@ -3115,23 +3097,23 @@ void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode*>>& contact_n
break; break;
std::vector<std::pair<Node*, int>> map_downwards_old; std::vector<std::pair<SupportNode*, int>> map_downwards_old;
std::vector<std::pair<Node*, int>> map_downwards_new; std::vector<std::pair<SupportNode*, int>> map_downwards_new;
linear_data_layers.emplace_back(0); linear_data_layers.emplace_back(0);
for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(contact_nodes.size()); ++layer_idx) { for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(contact_nodes.size()); ++layer_idx) {
std::sort(map_downwards_old.begin(), map_downwards_old.end(), [](auto& l, auto& r) { return l.first < r.first; }); std::sort(map_downwards_old.begin(), map_downwards_old.end(), [](auto& l, auto& r) { return l.first < r.first; });
auto& layer = contact_nodes[layer_idx]; auto& layer = contact_nodes[layer_idx];
for (size_t elem_idx = 0; elem_idx < layer.size(); ++elem_idx) { for (size_t elem_idx = 0; elem_idx < layer.size(); ++elem_idx) {
Node* node = layer[elem_idx]; SupportNode* node = layer[elem_idx];
int child = -1; int child = -1;
if (layer_idx > 0) { if (layer_idx > 0) {
auto it = std::lower_bound(map_downwards_old.begin(), map_downwards_old.end(), node, [](auto& l, const Node* r) { return l.first < r; }); auto it = std::lower_bound(map_downwards_old.begin(), map_downwards_old.end(), node, [](auto& l, const SupportNode* r) { return l.first < r; });
if (it != map_downwards_old.end() && it->first == node) { if (it != map_downwards_old.end() && it->first == node) {
child = it->second; child = it->second;
// Only one link points to a node above from below. // Only one link points to a node above from below.
assert(!(++it != map_downwards_old.end() && it->first == node)); assert(!(++it != map_downwards_old.end() && it->first == node));
} }
const Node* pchild = child == -1 ? nullptr : contact_nodes[layer_idx - 1][child]; const SupportNode* pchild = child == -1 ? nullptr : contact_nodes[layer_idx - 1][child];
} }
TreeSupport3D::SupportElement* elem = &move_bounds[node2elemIdx[node]]; TreeSupport3D::SupportElement* elem = &move_bounds[node2elemIdx[node]];
if (node->parent) { if (node->parent) {
@ -3159,7 +3141,7 @@ void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode*>>& contact_n
} }
#endif #endif
std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::vector<SupportNode *>> &contact_nodes) std::vector<LayerHeightData> TreeSupport::plan_layer_heights()
{ {
std::vector<LayerHeightData> layer_heights(contact_nodes.size()); std::vector<LayerHeightData> layer_heights(contact_nodes.size());
std::map<int, std::pair<coordf_t,coordf_t>> bounds; // layer_nr:(print_z, height) std::map<int, std::pair<coordf_t,coordf_t>> bounds; // layer_nr:(print_z, height)
@ -3257,7 +3239,7 @@ std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::ve
return layer_heights; return layer_heights;
} }
void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes) void TreeSupport::generate_contact_points()
{ {
const PrintObjectConfig &config = m_object->config(); const PrintObjectConfig &config = m_object->config();
const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value);
@ -3315,8 +3297,11 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
// fix bug of generating support for very thin objects // fix bug of generating support for very thin objects
if (m_object->layers().size() <= z_distance_top_layers + 1) if (m_object->layers().size() <= z_distance_top_layers + 1)
return; return;
//if (m_object->support_layer_count() <= m_raft_layers)
// return; contact_nodes.clear();
contact_nodes.resize(m_object->layers().size());
tbb::spin_mutex mtx;
int nonempty_layers = 0; int nonempty_layers = 0;
tbb::concurrent_vector<Slic3r::Vec3f> all_nodes; tbb::concurrent_vector<Slic3r::Vec3f> all_nodes;

View File

@ -405,6 +405,7 @@ private:
* Lazily computes volumes as needed. * Lazily computes volumes as needed.
* \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks * \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks
*/ */
std::vector<std::vector<SupportNode*>> contact_nodes;
std::shared_ptr<TreeSupportData> m_ts_data; std::shared_ptr<TreeSupportData> m_ts_data;
std::unique_ptr<TreeSupport3D::TreeModelVolumes> m_model_volumes; std::unique_ptr<TreeSupport3D::TreeModelVolumes> m_model_volumes;
PrintObject *m_object; PrintObject *m_object;
@ -440,7 +441,7 @@ private:
* save the resulting support polygons to. * save the resulting support polygons to.
* \param contact_nodes The nodes to draw as support. * \param contact_nodes The nodes to draw as support.
*/ */
void draw_circles(const std::vector<std::vector<SupportNode*>>& contact_nodes); void draw_circles();
/*! /*!
* \brief Drops down the nodes of the tree support towards the build plate. * \brief Drops down the nodes of the tree support towards the build plate.
@ -454,18 +455,18 @@ private:
* dropped down. The nodes are dropped to lower layers inside the same * dropped down. The nodes are dropped to lower layers inside the same
* vector of layers. * vector of layers.
*/ */
void drop_nodes(std::vector<std::vector<SupportNode *>> &contact_nodes); void drop_nodes();
void smooth_nodes(std::vector<std::vector<SupportNode *>> &contact_nodes); void smooth_nodes();
void smooth_nodes(std::vector<std::vector<SupportNode*>>& contact_nodes, const TreeSupport3D::TreeSupportSettings& config); void smooth_nodes(const TreeSupport3D::TreeSupportSettings& config);
/*! BBS: MusangKing: maximum layer height /*! BBS: MusangKing: maximum layer height
* \brief Optimize the generation of tree support by pre-planning the layer_heights * \brief Optimize the generation of tree support by pre-planning the layer_heights
* *
*/ */
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<SupportNode *>> &contact_nodes); std::vector<LayerHeightData> plan_layer_heights();
/*! /*!
* \brief Creates points where support contacts the model. * \brief Creates points where support contacts the model.
* *
@ -479,7 +480,7 @@ private:
* \return For each layer, a list of points where the tree should connect * \return For each layer, a list of points where the tree should connect
* with the model. * with the model.
*/ */
void generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes); void generate_contact_points();
/*! /*!
* \brief Add a node to the next layer. * \brief Add a node to the next layer.

View File

@ -19,7 +19,7 @@
#include "Polygon.hpp" #include "Polygon.hpp"
#include "Polyline.hpp" #include "Polyline.hpp"
#include "MutablePolygon.hpp" #include "MutablePolygon.hpp"
#include "SupportMaterial.hpp" #include "SupportCommon.hpp"
#include "TriangleMeshSlicer.hpp" #include "TriangleMeshSlicer.hpp"
#include "TreeSupport.hpp" #include "TreeSupport.hpp"
#include "I18N.hpp" #include "I18N.hpp"
@ -63,16 +63,6 @@ namespace Slic3r
namespace TreeSupport3D namespace TreeSupport3D
{ {
enum class LineStatus
{
INVALID,
TO_MODEL,
TO_MODEL_GRACIOUS,
TO_MODEL_GRACIOUS_SAFE,
TO_BP,
TO_BP_SAFE
};
using LineInformation = std::vector<std::pair<Point, LineStatus>>; using LineInformation = std::vector<std::pair<Point, LineStatus>>;
using LineInformations = std::vector<LineInformation>; using LineInformations = std::vector<LineInformation>;
using namespace std::literals; using namespace std::literals;
@ -350,6 +340,28 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
return max_layer; return max_layer;
} }
// picked from convert_lines_to_internal()
[[nodiscard]] LineStatus get_avoidance_status(const Point& p, coord_t radius, LayerIndex layer_idx,
const TreeModelVolumes& volumes, const TreeSupportSettings& config)
{
const bool min_xy_dist = config.xy_distance > config.xy_min_distance;
LineStatus type = LineStatus::INVALID;
if (!contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, false, min_xy_dist), p))
type = LineStatus::TO_BP_SAFE;
else if (!contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::Fast, false, min_xy_dist), p))
type = LineStatus::TO_BP;
else if (config.support_rests_on_model && !contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, true, min_xy_dist), p))
type = LineStatus::TO_MODEL_GRACIOUS_SAFE;
else if (config.support_rests_on_model && !contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::Fast, true, min_xy_dist), p))
type = LineStatus::TO_MODEL_GRACIOUS;
else if (config.support_rests_on_model && !contains(volumes.getCollision(radius, layer_idx, min_xy_dist), p))
type = LineStatus::TO_MODEL;
return type;
}
/*! /*!
* \brief Converts a Polygons object representing a line into the internal format. * \brief Converts a Polygons object representing a line into the internal format.
* *
@ -1201,15 +1213,6 @@ void sample_overhang_area(
} }
} }
inline SupportGeneratorLayer& layer_allocate(
SupportGeneratorLayerStorage& layer_storage,
SupporLayerType layer_type,
const SlicingParameters &slicing_params,
size_t layer_idx)
{
auto& layer = layer_storage.allocate(layer_type);
return layer_initialize(layer, layer_type, slicing_params, layer_idx);
}
/*! /*!
* \brief Creates the initial influence areas (that can later be propagated down) by placing them below the overhang. * \brief Creates the initial influence areas (that can later be propagated down) by placing them below the overhang.
@ -1932,7 +1935,7 @@ static void increase_areas_one_layer(
inc_wo_collision.clear(); inc_wo_collision.clear();
if (!settings.no_error) { if (!settings.no_error) {
// ERROR CASE // ERROR CASE
// if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, it still actually has an area that can be increased // if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if it would be a line wrongly, it still actually has an area that can be increased
Polygons lines_offset = offset(to_polylines(parent.influence_area), scaled<float>(0.005), jtMiter, 1.2); Polygons lines_offset = offset(to_polylines(parent.influence_area), scaled<float>(0.005), jtMiter, 1.2);
Polygons base_error_area = union_(parent.influence_area, lines_offset); Polygons base_error_area = union_(parent.influence_area, lines_offset);
result = increase_single_area(volumes, config, settings, layer_idx, parent, result = increase_single_area(volumes, config, settings, layer_idx, parent,
@ -4097,7 +4100,7 @@ void slice_branches(
if (! slices[layer_idx].empty()) { if (! slices[layer_idx].empty()) {
SupportGeneratorLayer *&l = intermediate_layers[layer_idx]; SupportGeneratorLayer *&l = intermediate_layers[layer_idx];
if (l == nullptr) if (l == nullptr)
l = &layer_allocate(layer_storage, SupporLayerType::sltBase, slicing_params, layer_idx); l = &layer_allocate(layer_storage, SupporLayerType::sltBase, slicing_params, config, layer_idx);
append(l->polygons, to_polygons(std::move(slices[layer_idx]))); append(l->polygons, to_polygons(std::move(slices[layer_idx])));
} }
@ -4312,7 +4315,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
// Don't fill in the tree supports, make them hollow with just a single sheath line. // Don't fill in the tree supports, make them hollow with just a single sheath line.
print.set_status(69, _L("Generating support")); print.set_status(69, _L("Generating support"));
generate_support_toolpaths(print_object, print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(), generate_support_toolpaths(print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(),
raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
#if 0 #if 0
@ -4785,7 +4788,9 @@ void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_suppo
} }
Points bedpts = tree_support->m_machine_border.contour.points; Points bedpts = tree_support->m_machine_border.contour.points;
BuildVolume build_volume{ Pointfs{ unscaled(bedpts[0]), unscaled(bedpts[1]),unscaled(bedpts[2]),unscaled(bedpts[3])}, tree_support->m_print_config->printable_height }; Pointfs bedptsf;
std::transform(bedpts.begin(), bedpts.end(), std::back_inserter(bedptsf), [](const Point &p) { return unscale(p); });
BuildVolume build_volume{ bedptsf, tree_support->m_print_config->printable_height };
TreeSupport3D::generate_support_areas(*print_object.print(), tree_support, build_volume, { idx }, throw_on_cancel); TreeSupport3D::generate_support_areas(*print_object.print(), tree_support, build_volume, { idx }, throw_on_cancel);
} }

View File

@ -45,8 +45,6 @@ using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
namespace TreeSupport3D namespace TreeSupport3D
{ {
// The number of vertices in each circle.
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
struct AreaIncreaseSettings struct AreaIncreaseSettings
{ {

View File

@ -6,11 +6,12 @@
#include "../BoundingBox.hpp" #include "../BoundingBox.hpp"
#include "../Utils.hpp" #include "../Utils.hpp"
#include "../Slicing.hpp" // SlicingParams #include "../Slicing.hpp" // SlicingParams
#include "TreeModelVolumes.hpp"
#include "SupportLayer.hpp" #include "SupportLayer.hpp"
#include "SupportParameters.hpp" #include "SupportParameters.hpp"
namespace Slic3r namespace Slic3r
{ {
// The number of vertices in each circle.
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
namespace TreeSupport3D namespace TreeSupport3D
{ {
using LayerIndex = int; using LayerIndex = int;
@ -78,6 +79,7 @@ struct TreeSupportMeshGroupSettings {
double support_tree_angle_slow = 25;// TODO add a setting? double support_tree_angle_slow = 25;// TODO add a setting?
double support_tree_branch_diameter_angle = 5; // TODO add a setting? double support_tree_branch_diameter_angle = 5; // TODO add a setting?
double tree_support_tip_diameter = 0.8; double tree_support_tip_diameter = 0.8;
this->support_tree_branch_distance = scaled<coord_t>(config.tree_support_branch_distance.value);
this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON); this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_angle_slow = std::clamp<double>(support_tree_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON); this->support_tree_angle_slow = std::clamp<double>(support_tree_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter.value); this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter.value);
@ -728,5 +730,16 @@ private:
std::mutex m_mutex_layer_storage; std::mutex m_mutex_layer_storage;
}; };
enum class LineStatus
{
INVALID,
TO_MODEL,
TO_MODEL_GRACIOUS,
TO_MODEL_GRACIOUS_SAFE,
TO_BP,
TO_BP_SAFE
};
} // namespace TreeSupport3D } // namespace TreeSupport3D
} // namespace slic3r } // namespace slic3r

View File

@ -1449,44 +1449,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
new_conf.set_key_value("support_style", new ConfigOptionEnum<SupportMaterialStyle>(smsDefault)); new_conf.set_key_value("support_style", new ConfigOptionEnum<SupportMaterialStyle>(smsDefault));
m_config_manipulation.apply(m_config, &new_conf); m_config_manipulation.apply(m_config, &new_conf);
} }
#if 0
// BBS popup a message to ask the user to set optimum parameters for tree support
if (opt_key == "support_type" || opt_key == "support_style") {
if (is_tree_slim(m_config->opt_enum<SupportType>("support_type"), m_config->opt_enum<SupportMaterialStyle>("support_style")) &&
!(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_int("support_interface_top_layers") == 0 && m_config->opt_int("tree_support_wall_count") == 2)) {
wxString msg_text = _L("We have added an experimental style \"Tree Slim\" that features smaller support volume but weaker strength.\n"
"We recommend using it with: 0 interface layers, 0 top distance, 2 walls.");
msg_text += "\n\n" + _L("Change these settings automatically? \n"
"Yes - Change these settings automatically\n"
"No - Do not change these settings for me");
MessageDialog dialog(wxGetApp().plater(), msg_text, "Suggestion", wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config;
if (dialog.ShowModal() == wxID_YES) {
new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0));
new_conf.set_key_value("support_interface_top_layers", new ConfigOptionInt(0));
new_conf.set_key_value("tree_support_wall_count", new ConfigOptionInt(2));
m_config_manipulation.apply(m_config, &new_conf);
}
wxGetApp().plater()->update();
} else if ((m_config->opt_enum<SupportType>("support_type")==stTreeAuto && (m_config->opt_enum<SupportMaterialStyle>("support_style")==smsTreeStrong || m_config->opt_enum<SupportMaterialStyle>("support_style") == smsTreeHybrid)) &&
!((m_config->opt_float("support_top_z_distance") >=0.1 || is_support_filament(m_config->opt_int("support_interface_filament") - 1))
&& m_config->opt_int("support_interface_top_layers") >1) ) {
wxString msg_text = _L("For \"Tree Strong\" and \"Tree Hybrid\" styles, we recommend the following settings: at least 2 interface layers, at least 0.1mm top z distance or using support materials on interface.");
msg_text += "\n\n" + _L("Change these settings automatically? \n"
"Yes - Change these settings automatically\n"
"No - Do not change these settings for me");
MessageDialog dialog(wxGetApp().plater(), msg_text, "Suggestion", wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config;
if (dialog.ShowModal() == wxID_YES) {
if (!is_support_filament(m_config->opt_int("support_interface_filament") - 1) && m_config->opt_float("support_top_z_distance") < 0.1)
new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0.2));
new_conf.set_key_value("support_interface_top_layers", new ConfigOptionInt(2));
m_config_manipulation.apply(m_config, &new_conf);
}
wxGetApp().plater()->update();
}
}
#endif
// BBS popup a message to ask the user to set optimum parameters for support interface if support materials are used // BBS popup a message to ask the user to set optimum parameters for support interface if support materials are used
if (opt_key == "support_interface_filament") { if (opt_key == "support_interface_filament") {
int interface_filament_id = m_config->opt_int("support_interface_filament") - 1; // the displayed id is based from 1, while internal id is based from 0 int interface_filament_id = m_config->opt_int("support_interface_filament") - 1; // the displayed id is based from 1, while internal id is based from 0