mirror of
https://git.mirrors.martin98.com/https://github.com/bambulab/BambuStudio.git
synced 2025-08-14 08:26:00 +08:00
FIX: organic support doesn't work with raft
Fix organic support issues by syncing with Prusa's changes. Thanks to Prusa. Jira: none Change-Id: I96fa1a939767bb0b0d2e8a5fd72966bb10a2042e (cherry picked from commit 89607dc73313e1b5d389674ed2d8f4f358bcb8b9)
This commit is contained in:
parent
b137b4a4ac
commit
a006286465
3
.gitignore
vendored
3
.gitignore
vendored
@ -24,3 +24,6 @@ deps/build-linux/*
|
||||
install_*
|
||||
build_*/
|
||||
SVG
|
||||
**/process_full/
|
||||
**/machine_full/
|
||||
**/filament_full/
|
||||
|
@ -265,14 +265,15 @@ set(lisbslic3r_sources
|
||||
SlicesToTriangleMesh.cpp
|
||||
SlicingAdaptive.cpp
|
||||
SlicingAdaptive.hpp
|
||||
SupportMaterial.cpp
|
||||
SupportMaterial.hpp
|
||||
TreeSupport.hpp
|
||||
TreeSupport.cpp
|
||||
TreeSupport3D.hpp
|
||||
TreeSupport3D.cpp
|
||||
TreeModelVolumes.hpp
|
||||
TreeModelVolumes.cpp
|
||||
Support/SupportMaterial.cpp
|
||||
Support/SupportMaterial.hpp
|
||||
Support/TreeSupport.hpp
|
||||
Support/TreeSupport.cpp
|
||||
Support/TreeSupport3D.hpp
|
||||
Support/TreeSupport3D.cpp
|
||||
Support/TreeModelVolumes.hpp
|
||||
Support/TreeModelVolumes.cpp
|
||||
Support/TreeSupportCommon.hpp
|
||||
MinimumSpanningTree.hpp
|
||||
MinimumSpanningTree.cpp
|
||||
Surface.cpp
|
||||
@ -500,6 +501,7 @@ target_link_libraries(libslic3r
|
||||
qhull
|
||||
semver
|
||||
TBB::tbb
|
||||
TBB::tbbmalloc
|
||||
libslic3r_cgal
|
||||
${CMAKE_DL_LIBS}
|
||||
PNG::PNG
|
||||
|
@ -677,6 +677,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c
|
||||
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
|
||||
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return intersection(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
|
@ -491,6 +491,9 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox().
|
||||
// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon.
|
||||
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "Support/SupportMaterial.hpp"
|
||||
#include "Thread.hpp"
|
||||
#include "Time.hpp"
|
||||
#include "GCode.hpp"
|
||||
|
@ -7,7 +7,8 @@
|
||||
#include "I18N.hpp"
|
||||
#include "Layer.hpp"
|
||||
#include "MutablePolygon.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "Support/SupportMaterial.hpp"
|
||||
#include "Support/TreeSupport.hpp"
|
||||
#include "Surface.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
@ -17,7 +18,6 @@
|
||||
#include "Fill/FillLightning.hpp"
|
||||
#include "Format/STL.hpp"
|
||||
#include "InternalBridgeDetector.hpp"
|
||||
#include "TreeSupport.hpp"
|
||||
|
||||
#include <float.h>
|
||||
#include <string_view>
|
||||
|
151
src/libslic3r/Support/SupportLayer.hpp
Normal file
151
src/libslic3r/Support/SupportLayer.hpp
Normal file
@ -0,0 +1,151 @@
|
||||
#pragma once
|
||||
|
||||
#include <oneapi/tbb/scalable_allocator.h>
|
||||
#include <oneapi/tbb/spin_mutex.h>
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Slicing.hpp"
|
||||
#include "../Fill/FillBase.hpp"
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../Polygon.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PrintObject;
|
||||
class PrintConfig;
|
||||
class PrintObjectConfig;
|
||||
|
||||
// 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 SupportGeneratorLayer is aware of the bridging flow and the interface gaps between the object and the support.
|
||||
// This is from the old "MyLayer".
|
||||
class SupportGeneratorLayer
|
||||
{
|
||||
public:
|
||||
void reset() {
|
||||
*this = SupportGeneratorLayer();
|
||||
}
|
||||
|
||||
bool operator==(const SupportGeneratorLayer& 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 SupportGeneratorLayer& 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(SupportGeneratorLayer&& 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 == SupporLayerType::sltTopContact) ? this->bottom_z : this->print_z; }
|
||||
|
||||
SupporLayerType layer_type{ SupporLayerType::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;
|
||||
};
|
||||
|
||||
// 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.
|
||||
class SupportGeneratorLayerStorage {
|
||||
public:
|
||||
SupportGeneratorLayer& allocate_unguarded(SupporLayerType layer_type) {
|
||||
m_storage.emplace_back();
|
||||
m_storage.back().layer_type = layer_type;
|
||||
return m_storage.back();
|
||||
}
|
||||
|
||||
SupportGeneratorLayer& allocate(SupporLayerType layer_type)
|
||||
{
|
||||
m_mutex.lock();
|
||||
m_storage.emplace_back();
|
||||
SupportGeneratorLayer *layer_new = &m_storage.back();
|
||||
m_mutex.unlock();
|
||||
layer_new->layer_type = layer_type;
|
||||
return *layer_new;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename BaseType>
|
||||
using Allocator = tbb::scalable_allocator<BaseType>;
|
||||
Slic3r::deque<SupportGeneratorLayer, Allocator<SupportGeneratorLayer>> m_storage;
|
||||
tbb::spin_mutex m_mutex;
|
||||
};
|
||||
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
|
||||
} // namespace Slic3r
|
@ -343,86 +343,6 @@ static std::string get_svg_filename(std::string layer_nr_or_z, std::string tag
|
||||
return prefix + tag + "_" + layer_nr_or_z /*+ "_" + std::to_string(rand_num)*/ + suffix;
|
||||
}
|
||||
|
||||
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->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));
|
||||
|
||||
// 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.value;
|
||||
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 are 0-based but object_config.support_filament's are 1-based
|
||||
object_extruders[0]+1 == 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;
|
||||
}
|
||||
|
||||
|
||||
this->base_angle = Geometry::deg2rad(float(object_config.support_threshold_angle.value));
|
||||
this->interface_angle = Geometry::deg2rad(float(object_config.support_threshold_angle.value + 90.));
|
||||
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->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);
|
||||
if (object_config.support_interface_top_layers.value == 0) {
|
||||
// No interface layers allowed, print everything with the base support pattern.
|
||||
this->interface_spacing = this->support_spacing;
|
||||
this->interface_density = this->support_density;
|
||||
}
|
||||
|
||||
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
|
||||
this->with_sheath = object_config.tree_support_wall_count>0;
|
||||
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);
|
||||
if (object_config.support_interface_pattern == smipGrid)
|
||||
this->contact_fill_pattern = ipGrid;
|
||||
else if (object_config.support_interface_pattern == smipRectilinearInterlaced)
|
||||
this->contact_fill_pattern = ipRectilinear;
|
||||
else
|
||||
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);
|
||||
}
|
||||
|
||||
PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) :
|
||||
m_print_config (&object->print()->config()),
|
||||
m_object_config (&object->config()),
|
||||
@ -570,7 +490,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
|
||||
|
||||
// Propagate top / bottom contact layers to generate interface layers
|
||||
// and base interface layers (for soluble interface / non souble base only)
|
||||
auto [interface_layers, base_interface_layers] = this->generate_interface_layers(bottom_contacts, top_contacts, intermediate_layers, layer_storage);
|
||||
SupportGeneratorLayersPtr empty_layers;
|
||||
auto [interface_layers, base_interface_layers] = generate_interface_layers(*m_object_config, m_support_params, bottom_contacts, top_contacts, empty_layers, empty_layers, intermediate_layers, layer_storage);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating raft";
|
||||
|
||||
@ -1818,8 +1739,7 @@ static inline std::pair<SupportGeneratorLayer*, SupportGeneratorLayer*> new_cont
|
||||
const SlicingParameters &slicing_params,
|
||||
const coordf_t support_layer_height_min,
|
||||
const Layer &layer,
|
||||
std::deque<SupportGeneratorLayer> &layer_storage,
|
||||
tbb::spin_mutex &layer_storage_mutex)
|
||||
SupportGeneratorLayerStorage &layer_storage)
|
||||
{
|
||||
double print_z, bottom_z, height;
|
||||
SupportGeneratorLayer* bridging_layer = nullptr;
|
||||
@ -1891,7 +1811,7 @@ static inline std::pair<SupportGeneratorLayer*, SupportGeneratorLayer*> new_cont
|
||||
}
|
||||
if (bridging_print_z < print_z - EPSILON) {
|
||||
// Allocate the new layer.
|
||||
bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::sltTopContact);
|
||||
bridging_layer = &layer_storage.allocate(SupporLayerType::sltTopContact);
|
||||
bridging_layer->idx_object_layer_above = layer_id;
|
||||
bridging_layer->print_z = bridging_print_z;
|
||||
if (bridging_print_z == slicing_params.first_print_layer_height) {
|
||||
@ -1909,7 +1829,7 @@ static inline std::pair<SupportGeneratorLayer*, SupportGeneratorLayer*> new_cont
|
||||
}
|
||||
}
|
||||
|
||||
SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::sltTopContact);
|
||||
SupportGeneratorLayer &new_layer = layer_storage.allocate(SupporLayerType::sltTopContact);
|
||||
new_layer.idx_object_layer_above = layer_id;
|
||||
new_layer.print_z = print_z;
|
||||
new_layer.bottom_z = bottom_z;
|
||||
@ -2216,7 +2136,6 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers(
|
||||
// For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow,
|
||||
// and the other for the overhangs extruded with a normal flow.
|
||||
contact_out.assign(num_layers * 2, nullptr);
|
||||
tbb::spin_mutex layer_storage_mutex;
|
||||
|
||||
std::vector<ExPolygons> overhangs_per_layers(num_layers);
|
||||
size_t layer_id_start = this->has_raft() ? 0 : 1;
|
||||
@ -2421,7 +2340,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers(
|
||||
// Now apply the contact areas to the layer where they need to be made.
|
||||
if (!contact_polygons.empty() || !overhang_polygons.empty()) {
|
||||
// Allocate the two empty layers.
|
||||
auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, m_support_params.support_layer_height_min, layer, layer_storage, layer_storage_mutex);
|
||||
auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, m_support_params.support_layer_height_min, layer, layer_storage);
|
||||
if (new_layer) {
|
||||
// Fill the non-bridging layer with polygons.
|
||||
fill_contact_layer(*new_layer, layer_id, m_slicing_params,
|
||||
@ -2470,7 +2389,7 @@ static inline SupportGeneratorLayer* detect_bottom_contacts(
|
||||
// First top contact layer index overlapping with this new bottom interface layer.
|
||||
size_t contact_idx,
|
||||
// To allocate a new layer from.
|
||||
std::deque<SupportGeneratorLayer> &layer_storage,
|
||||
SupportGeneratorLayerStorage &layer_storage,
|
||||
// To trim the support areas above this bottom interface layer with this newly created bottom interface layer.
|
||||
std::vector<Polygons> &layer_support_areas,
|
||||
// Support areas projected from top to bottom, starting with top support interfaces.
|
||||
@ -2505,7 +2424,7 @@ static inline SupportGeneratorLayer* detect_bottom_contacts(
|
||||
size_t layer_id = layer.id() - slicing_params.raft_layers();
|
||||
|
||||
// Allocate a new bottom contact layer.
|
||||
SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, SupporLayerType::sltBottomContact);
|
||||
SupportGeneratorLayer &layer_new = layer_storage.allocate_unguarded(SupporLayerType::sltBottomContact);
|
||||
// Grow top surfaces so that interface and support generation are generated
|
||||
// with some spacing from object - it looks we don't need the actual
|
||||
// top shapes so this can be done here
|
||||
@ -2960,7 +2879,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp
|
||||
assert(std::abs(extr2->bottom_z - m_slicing_params.first_print_layer_height) < EPSILON);
|
||||
assert(extr2->print_z >= m_slicing_params.first_print_layer_height + m_support_params.support_layer_height_min - EPSILON);
|
||||
if (intermediate_layers.empty() || intermediate_layers.back()->print_z < m_slicing_params.first_print_layer_height) {
|
||||
SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
|
||||
SupportGeneratorLayer &layer_new = layer_storage.allocate(sltIntermediate);
|
||||
layer_new.bottom_z = 0.;
|
||||
layer_new.print_z = m_slicing_params.first_print_layer_height;
|
||||
layer_new.height = m_slicing_params.first_print_layer_height;
|
||||
@ -2982,7 +2901,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp
|
||||
// At this point only layers above first_print_layer_heigth + EPSILON are expected as the other cases were captured earlier.
|
||||
assert(extr2z >= m_slicing_params.first_print_layer_height + EPSILON);
|
||||
// Generate a new intermediate layer.
|
||||
SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
|
||||
SupportGeneratorLayer &layer_new = layer_storage.allocate(sltIntermediate);
|
||||
layer_new.bottom_z = 0.;
|
||||
layer_new.print_z = extr1z = m_slicing_params.first_print_layer_height;
|
||||
layer_new.height = extr1z;
|
||||
@ -3002,7 +2921,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp
|
||||
++ idx_layer_object;
|
||||
if (idx_layer_object == 0 && extr1z == m_slicing_params.raft_interface_top_z) {
|
||||
// Insert one base support layer below the object.
|
||||
SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
|
||||
SupportGeneratorLayer &layer_new = layer_storage.allocate(sltIntermediate);
|
||||
layer_new.print_z = m_slicing_params.object_print_z_min;
|
||||
layer_new.bottom_z = m_slicing_params.raft_interface_top_z;
|
||||
layer_new.height = layer_new.print_z - layer_new.bottom_z;
|
||||
@ -3010,7 +2929,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp
|
||||
}
|
||||
// Emit all intermediate support layers synchronized with object layers up to extr2z.
|
||||
for (; idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) {
|
||||
SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
|
||||
SupportGeneratorLayer &layer_new = layer_storage.allocate(sltIntermediate);
|
||||
layer_new.print_z = object.layers()[idx_layer_object]->print_z;
|
||||
layer_new.height = object.layers()[idx_layer_object]->height;
|
||||
layer_new.bottom_z = (idx_layer_object > 0) ? object.layers()[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height);
|
||||
@ -3028,7 +2947,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp
|
||||
// between the 1st intermediate layer print_z and extr1->print_z is not too small.
|
||||
assert(extr1->bottom_z + m_support_params.support_layer_height_min < extr1->print_z + EPSILON);
|
||||
// Generate the first intermediate layer.
|
||||
SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
|
||||
SupportGeneratorLayer &layer_new = layer_storage.allocate(sltIntermediate);
|
||||
layer_new.bottom_z = extr1->bottom_z;
|
||||
layer_new.print_z = extr1z = extr1->print_z;
|
||||
layer_new.height = extr1->height;
|
||||
@ -3052,7 +2971,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp
|
||||
coordf_t extr2z_large_steps = extr2z;
|
||||
// Take the largest allowed step in the Z axis until extr2z_large_steps is reached.
|
||||
for (size_t i = 0; i < n_layers_extra; ++ i) {
|
||||
SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
|
||||
SupportGeneratorLayer &layer_new = layer_storage.allocate(sltIntermediate);
|
||||
if (i + 1 == n_layers_extra) {
|
||||
// Last intermediate layer added. Align the last entered layer with extr2z_large_steps exactly.
|
||||
layer_new.bottom_z = (i == 0) ? extr1z : intermediate_layers.back()->print_z;
|
||||
@ -3456,7 +3375,7 @@ SupportGeneratorLayersPtr generate_raft_base(
|
||||
// Do not add the raft contact layer, only add the raft layers below the contact layer.
|
||||
// Insert the 1st layer.
|
||||
{
|
||||
SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, (slicing_params.base_raft_layers > 0) ? sltRaftBase : sltRaftInterface);
|
||||
SupportGeneratorLayer &new_layer = layer_storage.allocate((slicing_params.base_raft_layers > 0) ? sltRaftBase : sltRaftInterface);
|
||||
raft_layers.push_back(&new_layer);
|
||||
new_layer.print_z = slicing_params.first_print_layer_height;
|
||||
new_layer.height = slicing_params.first_print_layer_height;
|
||||
@ -3466,7 +3385,7 @@ SupportGeneratorLayersPtr generate_raft_base(
|
||||
// Insert the base layers.
|
||||
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
|
||||
coordf_t print_z = raft_layers.back()->print_z;
|
||||
SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, SupporLayerType::sltRaftBase);
|
||||
SupportGeneratorLayer &new_layer = layer_storage.allocate(SupporLayerType::sltRaftBase);
|
||||
raft_layers.push_back(&new_layer);
|
||||
new_layer.print_z = print_z + slicing_params.base_raft_layer_height;
|
||||
new_layer.height = slicing_params.base_raft_layer_height;
|
||||
@ -3476,7 +3395,7 @@ SupportGeneratorLayersPtr generate_raft_base(
|
||||
// Insert the interface layers.
|
||||
for (size_t i = 1; i < slicing_params.interface_raft_layers; ++ i) {
|
||||
coordf_t print_z = raft_layers.back()->print_z;
|
||||
SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, SupporLayerType::sltRaftInterface);
|
||||
SupportGeneratorLayer &new_layer = layer_storage.allocate(SupporLayerType::sltRaftInterface);
|
||||
raft_layers.push_back(&new_layer);
|
||||
new_layer.print_z = print_z + slicing_params.interface_raft_layer_height;
|
||||
new_layer.height = slicing_params.interface_raft_layer_height;
|
||||
@ -3522,28 +3441,24 @@ SupportGeneratorLayersPtr generate_raft_base(
|
||||
}
|
||||
|
||||
// Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers.
|
||||
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> PrintObjectSupportMaterial::generate_interface_layers(
|
||||
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
|
||||
const PrintObjectConfig& config,
|
||||
const SupportParameters& m_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,
|
||||
// Input, will be trimmed with the newly created interface layers.
|
||||
SupportGeneratorLayersPtr &intermediate_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage) const
|
||||
SupportGeneratorLayerStorage &layer_storage)
|
||||
{
|
||||
// my $area_threshold = $self->interface_flow->scaled_spacing ** 2;
|
||||
|
||||
const PrintObjectConfig* m_object_config = &config;
|
||||
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> base_and_interface_layers;
|
||||
SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first;
|
||||
SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second;
|
||||
|
||||
// distinguish between interface and base interface layers
|
||||
// Contact layer is considered an interface layer, therefore run the following block only if support_interface_top_layers > 1.
|
||||
// Contact layer needs a base_interface layer, therefore run the following block if support_interface_top_layers > 0, has soluble support and extruders are different.
|
||||
bool soluble_interface_non_soluble_base =
|
||||
// Zero z-gap between the overhangs and the support interface.
|
||||
m_slicing_params.soluble_interface &&
|
||||
// Interface extruder soluble.
|
||||
m_object_config->support_interface_filament.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_interface_filament.value - 1) &&
|
||||
// Base extruder: Either "print with active extruder" not soluble.
|
||||
(m_object_config->support_filament.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_filament.value - 1));
|
||||
bool snug_supports = m_object_config->support_style.value == smsSnug;
|
||||
// BBS: if support interface and support base do not use the same filament, add a base layer to improve their adhesion
|
||||
bool differnt_support_interface_filament = m_object_config->support_filament != 0 && m_object_config->support_interface_filament != 0 && m_object_config->support_interface_filament.value != m_object_config->support_filament.value;
|
||||
@ -3568,11 +3483,11 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> PrintObjectSuppo
|
||||
auto smoothing_distance = m_support_params.support_material_interface_flow.scaled_spacing() * 1.5;
|
||||
auto minimum_island_radius = m_support_params.support_material_interface_flow.scaled_spacing() / m_support_params.interface_density;
|
||||
auto closing_distance = smoothing_distance; // scaled<float>(m_object_config->support_closing_radius.value);
|
||||
tbb::spin_mutex layer_storage_mutex;
|
||||
// Insert a new layer into base_interface_layers, if intersection with base exists.
|
||||
auto insert_layer = [&layer_storage, &layer_storage_mutex, snug_supports, closing_distance, smoothing_distance, minimum_island_radius](
|
||||
SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* {
|
||||
assert(! bottom.empty() || ! top.empty());
|
||||
auto insert_layer = [&layer_storage, snug_supports, closing_distance, smoothing_distance, minimum_island_radius](
|
||||
SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* {
|
||||
bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty();
|
||||
assert(! bottom.empty() || ! top.empty() || has_top_interface);
|
||||
// Merge top into bottom, unite them with a safety offset.
|
||||
append(bottom, std::move(top));
|
||||
// Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners).
|
||||
@ -3581,11 +3496,17 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> PrintObjectSuppo
|
||||
smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
|
||||
union_safety_offset(std::move(bottom)),
|
||||
intermediate_layer.polygons);
|
||||
if (has_top_interface) {
|
||||
// Don't trim the precomputed Organic supports top interface with base layer
|
||||
// as the precomputed top interface likely expands over multiple tree tips.
|
||||
bottom = union_(std::move(top_interface_layer->polygons), bottom);
|
||||
top_interface_layer->polygons.clear();
|
||||
}
|
||||
if (! bottom.empty()) {
|
||||
//FIXME Remove non-printable tiny islands, let them be printed using the base support.
|
||||
//bottom = opening(std::move(bottom), minimum_island_radius);
|
||||
if (! bottom.empty()) {
|
||||
SupportGeneratorLayer &layer_new = layer_allocate(layer_storage, layer_storage_mutex, type);
|
||||
SupportGeneratorLayer &layer_new = top_interface_layer ? *top_interface_layer : layer_storage.allocate(type);
|
||||
layer_new.polygons = std::move(bottom);
|
||||
layer_new.print_z = intermediate_layer.print_z;
|
||||
layer_new.bottom_z = intermediate_layer.bottom_z;
|
||||
@ -3604,7 +3525,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> PrintObjectSuppo
|
||||
return nullptr;
|
||||
};
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
|
||||
[&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer,
|
||||
[&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer,
|
||||
num_interface_layers_top, num_interface_layers_bottom, num_base_interface_layers_top, num_base_interface_layers_bottom, num_interface_layers_only_top, num_interface_layers_only_bottom,
|
||||
snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {
|
||||
// Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below
|
||||
@ -3613,6 +3534,10 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> PrintObjectSuppo
|
||||
auto idx_top_contact_first = -1;
|
||||
// Index of the first bottom contact layer intersecting the current intermediate layer.
|
||||
auto idx_bottom_contact_first = -1;
|
||||
// Index of the first top interface layer intersecting the current intermediate layer.
|
||||
auto idx_top_interface_first = -1;
|
||||
// Index of the first top contact interface layer intersecting the current intermediate layer.
|
||||
auto idx_top_base_interface_first = -1;
|
||||
auto num_intermediate = int(intermediate_layers.size());
|
||||
for (int idx_intermediate_layer = range.begin(); idx_intermediate_layer < range.end(); ++ idx_intermediate_layer) {
|
||||
SupportGeneratorLayer &intermediate_layer = *intermediate_layers[idx_intermediate_layer];
|
||||
@ -3664,23 +3589,55 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> PrintObjectSuppo
|
||||
polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons);
|
||||
}
|
||||
}
|
||||
SupportGeneratorLayer *interface_layer = nullptr;
|
||||
if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty()) {
|
||||
auto resolve_same_layer = [](SupportGeneratorLayersPtr &layers, int &idx, coordf_t print_z) -> SupportGeneratorLayer* {
|
||||
if (! layers.empty()) {
|
||||
idx = idx_higher_or_equal(layers, idx, [print_z](const SupportGeneratorLayer *layer) { return layer->print_z > print_z - EPSILON; });
|
||||
if (idx < int(layers.size()) && layers[idx]->print_z < print_z + EPSILON)
|
||||
return layers[idx];
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
SupportGeneratorLayer *top_interface_layer = resolve_same_layer(top_interface_layers, idx_top_interface_first, intermediate_layer.print_z);
|
||||
SupportGeneratorLayer *top_base_interface_layer = resolve_same_layer(top_base_interface_layers, idx_top_base_interface_first, intermediate_layer.print_z);
|
||||
SupportGeneratorLayer *interface_layer = nullptr;
|
||||
if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty() ||
|
||||
(top_interface_layer && ! top_interface_layer->polygons.empty())) {
|
||||
interface_layer = insert_layer(
|
||||
intermediate_layer, polygons_bottom_contact_projected_interface, std::move(polygons_top_contact_projected_interface), nullptr,
|
||||
intermediate_layer, polygons_bottom_contact_projected_interface, std::move(polygons_top_contact_projected_interface), top_interface_layer, nullptr,
|
||||
polygons_top_contact_projected_interface.empty() ? sltBottomInterface : sltTopInterface);
|
||||
interface_layers[idx_intermediate_layer] = interface_layer;
|
||||
}
|
||||
if (! polygons_bottom_contact_projected_base.empty() || ! polygons_top_contact_projected_base.empty())
|
||||
if (! polygons_bottom_contact_projected_base.empty() || ! polygons_top_contact_projected_base.empty() ||
|
||||
(top_base_interface_layer && ! top_base_interface_layer->polygons.empty()))
|
||||
base_interface_layers[idx_intermediate_layer] = insert_layer(
|
||||
intermediate_layer, polygons_bottom_contact_projected_base, std::move(polygons_top_contact_projected_base),
|
||||
intermediate_layer, polygons_bottom_contact_projected_base, std::move(polygons_top_contact_projected_base), top_base_interface_layer,
|
||||
interface_layer ? &interface_layer->polygons : nullptr, sltBase);
|
||||
}
|
||||
});
|
||||
|
||||
// Compress contact_out, remove the nullptr items.
|
||||
remove_nulls(interface_layers);
|
||||
remove_nulls(base_interface_layers);
|
||||
// The parallel_for above may not have merged all the interface and base_interface layers
|
||||
// generated by the Organic supports code, do it here.
|
||||
auto merge_remove_empty = [](SupportGeneratorLayersPtr& in1, SupportGeneratorLayersPtr& in2) {
|
||||
auto remove_empty = [](SupportGeneratorLayersPtr& vec) {
|
||||
vec.erase(
|
||||
std::remove_if(vec.begin(), vec.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr || ptr->polygons.empty(); }),
|
||||
vec.end());
|
||||
};
|
||||
remove_empty(in1);
|
||||
remove_empty(in2);
|
||||
if (in2.empty())
|
||||
return std::move(in1);
|
||||
else if (in1.empty())
|
||||
return std::move(in2);
|
||||
else {
|
||||
SupportGeneratorLayersPtr out(in1.size() + in2.size(), nullptr);
|
||||
std::merge(in1.begin(), in1.end(), in2.begin(), in2.end(), out.begin(), [](auto* l, auto* r) { return l->print_z < r->print_z; });
|
||||
return out;
|
||||
}
|
||||
};
|
||||
interface_layers = merge_remove_empty(interface_layers, top_interface_layers);
|
||||
base_interface_layers = merge_remove_empty(base_interface_layers, top_base_interface_layers);
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
|
||||
}
|
||||
|
@ -5,150 +5,14 @@
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include "Fill/FillBase.hpp"
|
||||
|
||||
#include "SupportLayer.hpp"
|
||||
#include "SupportParameters.hpp"
|
||||
namespace Slic3r {
|
||||
|
||||
class PrintObject;
|
||||
class PrintConfig;
|
||||
class PrintObjectConfig;
|
||||
|
||||
// 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 SupportGeneratorLayer is aware of the bridging flow and the interface gaps between the object and the support.
|
||||
// This is from the old "MyLayer".
|
||||
class SupportGeneratorLayer
|
||||
{
|
||||
public:
|
||||
void reset() {
|
||||
*this = SupportGeneratorLayer();
|
||||
}
|
||||
|
||||
bool operator==(const SupportGeneratorLayer &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 SupportGeneratorLayer &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(SupportGeneratorLayer &&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 == SupporLayerType::sltTopContact) ? this->bottom_z : this->print_z; }
|
||||
|
||||
SupporLayerType layer_type { SupporLayerType::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;
|
||||
};
|
||||
|
||||
// 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.
|
||||
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
|
||||
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
|
||||
|
||||
struct SupportParameters {
|
||||
SupportParameters(const PrintObject &object);
|
||||
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;
|
||||
};
|
||||
|
||||
using LayerIndex = int;
|
||||
|
||||
inline double layer_z(const SlicingParameters& slicing_params, const size_t layer_idx)
|
||||
@ -211,6 +75,20 @@ SupportGeneratorLayersPtr generate_support_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(
|
||||
@ -295,15 +173,7 @@ private:
|
||||
SupportGeneratorLayersPtr &intermediate_layers,
|
||||
const std::vector<Polygons> &layer_support_areas) 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<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
SupportGeneratorLayersPtr &intermediate_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage) const;
|
||||
|
||||
|
||||
|
||||
// Trim support layers by an object to leave a defined gap between
|
||||
// the support volume and the object.
|
165
src/libslic3r/Support/SupportParameters.hpp
Normal file
165
src/libslic3r/Support/SupportParameters.hpp
Normal file
@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
#include "../libslic3r.h"
|
||||
#include "../Flow.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Slicing.hpp"
|
||||
#include "../Fill/FillBase.hpp"
|
||||
#include "../Print.hpp"
|
||||
#include "../Layer.hpp"
|
||||
#include "SupportLayer.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
struct 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));
|
||||
|
||||
{
|
||||
this->num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value);
|
||||
this->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;
|
||||
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(int(num_top_interface_layers) / 2, 2));
|
||||
this->num_bottom_base_interface_layers = size_t(std::min(int(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));
|
||||
|
||||
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
||||
this->support_layer_height_min = scaled<coord_t>(0.01);
|
||||
for (auto lh : print_config.min_layer_height.values)
|
||||
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh));
|
||||
for (auto layer : object.layers())
|
||||
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height));
|
||||
|
||||
if (object_config.support_interface_top_layers.value == 0) {
|
||||
// No interface layers allowed, print everything with the base support pattern.
|
||||
this->support_material_interface_flow = this->support_material_flow;
|
||||
}
|
||||
|
||||
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
||||
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
||||
coordf_t external_perimeter_width = 0.;
|
||||
coordf_t bridge_flow_ratio = 0;
|
||||
for (size_t region_id = 0; region_id < object.num_printing_regions(); ++region_id) {
|
||||
const PrintRegion& region = object.printing_region(region_id);
|
||||
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width()));
|
||||
bridge_flow_ratio += region.config().bridge_flow;
|
||||
}
|
||||
this->gap_xy = object_config.support_object_xy_distance.value;
|
||||
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 are 0-based but object_config.support_filament's are 1-based
|
||||
object_extruders[0] + 1 == 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;
|
||||
}
|
||||
|
||||
|
||||
this->base_angle = Geometry::deg2rad(float(object_config.support_threshold_angle.value));
|
||||
this->interface_angle = Geometry::deg2rad(float(object_config.support_threshold_angle.value + 90.));
|
||||
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->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);
|
||||
if (object_config.support_interface_top_layers.value == 0) {
|
||||
// No interface layers allowed, print everything with the base support pattern.
|
||||
this->interface_spacing = this->support_spacing;
|
||||
this->interface_density = this->support_density;
|
||||
}
|
||||
|
||||
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
|
||||
this->with_sheath = object_config.tree_support_wall_count > 0;
|
||||
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);
|
||||
if (object_config.support_interface_pattern == smipGrid)
|
||||
this->contact_fill_pattern = ipGrid;
|
||||
else if (object_config.support_interface_pattern == smipRectilinearInterlaced)
|
||||
this->contact_fill_pattern = ipRectilinear;
|
||||
else
|
||||
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);
|
||||
}
|
||||
// Both top / bottom contacts and interfaces are soluble.
|
||||
bool soluble_interface;
|
||||
// Support contact & interface are soluble, but support base is non-soluble.
|
||||
bool soluble_interface_non_soluble_base;
|
||||
|
||||
// Is there at least a top contact layer extruded above support base?
|
||||
bool has_top_contacts;
|
||||
// Is there at least a bottom contact layer extruded below support base?
|
||||
bool has_bottom_contacts;
|
||||
|
||||
// Number of top interface layers without counting the contact layer.
|
||||
size_t num_top_interface_layers;
|
||||
// Number of bottom interface layers without counting the contact layer.
|
||||
size_t num_bottom_interface_layers;
|
||||
// Number of top base interface layers. Zero if not soluble_interface_non_soluble_base.
|
||||
size_t num_top_base_interface_layers;
|
||||
// Number of bottom base interface layers. Zero if not soluble_interface_non_soluble_base.
|
||||
size_t num_bottom_base_interface_layers;
|
||||
|
||||
bool has_contacts() const { return this->has_top_contacts || this->has_bottom_contacts; }
|
||||
bool has_interfaces() const { return this->num_top_interface_layers + this->num_bottom_interface_layers > 0; }
|
||||
bool has_base_interfaces() const { return this->num_top_base_interface_layers + this->num_bottom_base_interface_layers > 0; }
|
||||
size_t num_top_interface_layers_only() const { return this->num_top_interface_layers - this->num_top_base_interface_layers; }
|
||||
size_t num_bottom_interface_layers_only() const { return this->num_bottom_interface_layers - this->num_bottom_base_interface_layers; }
|
||||
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;
|
||||
};
|
||||
} // namespace Slic3r
|
@ -7,16 +7,17 @@
|
||||
// CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include "TreeModelVolumes.hpp"
|
||||
#include "TreeSupport3D.hpp"
|
||||
#include "TreeSupportCommon.hpp"
|
||||
|
||||
#include "BuildVolume.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Layer.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "../BuildVolume.hpp"
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../Flow.hpp"
|
||||
#include "../Layer.hpp"
|
||||
#include "../Point.hpp"
|
||||
#include "../Print.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "../format.hpp"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
@ -34,74 +35,6 @@ using namespace std::literals;
|
||||
// had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL()
|
||||
#define error_level_not_in_cache error
|
||||
|
||||
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.support_material);
|
||||
//assert(config.support_material_style == smsTree || config.support_material_style == smsOrganic);
|
||||
|
||||
// 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_height = config.support_interface_top_layers.value * this->layer_height;
|
||||
// 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 = 2.5 * line_width ??
|
||||
double support_tree_angle_slow = 25;// TODO add a setting?
|
||||
double support_tree_branch_diameter_angle = 5; // TODO add a setting?
|
||||
double tree_support_tip_diameter = 0.8;
|
||||
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_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter.value);
|
||||
this->support_tree_branch_diameter_angle = std::clamp<double>(support_tree_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
|
||||
this->support_tree_top_rate = 30; // percent
|
||||
// this->support_tree_tip_diameter = this->support_line_width;
|
||||
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(tree_support_tip_diameter), 0, this->support_tree_branch_diameter);
|
||||
}
|
||||
|
||||
//FIXME Machine border is currently ignored.
|
||||
static Polygons calculateMachineBorderCollision(Polygon machine_border)
|
||||
{
|
||||
@ -162,14 +95,23 @@ TreeModelVolumes::TreeModelVolumes(
|
||||
{
|
||||
m_anti_overhang = print_object.slice_support_blockers();
|
||||
TreeSupportMeshGroupSettings mesh_settings(print_object);
|
||||
m_layer_outlines.emplace_back(mesh_settings, std::vector<Polygons>{});
|
||||
const TreeSupportSettings config{ mesh_settings, print_object.slicing_parameters() };
|
||||
m_current_min_xy_dist = config.xy_min_distance;
|
||||
m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist;
|
||||
assert(m_current_min_xy_dist_delta >= 0);
|
||||
m_increase_until_radius = config.increase_radius_until_radius;
|
||||
m_radius_0 = config.getRadius(0);
|
||||
m_raft_layers = config.raft_layers;
|
||||
m_current_outline_idx = 0;
|
||||
m_layer_outlines.emplace_back(mesh_settings, std::vector<Polygons>{});
|
||||
std::vector<Polygons> &outlines = m_layer_outlines.front().second;
|
||||
outlines.assign(print_object.layer_count(), Polygons{});
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, print_object.layer_count()),
|
||||
size_t num_raft_layers = m_raft_layers.size();
|
||||
size_t num_layers = print_object.layer_count() + num_raft_layers;
|
||||
outlines.assign(num_layers, Polygons{});
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers, num_layers, std::min<size_t>(1, std::max<size_t>(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))),
|
||||
[&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
|
||||
outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx)->lslices, mesh_settings.resolution));
|
||||
outlines[layer_idx] = polygons_simplify(to_polygons(print_object.get_layer(layer_idx - num_raft_layers)->lslices), mesh_settings.resolution);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
@ -181,12 +123,6 @@ TreeModelVolumes::TreeModelVolumes(
|
||||
m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution);
|
||||
}
|
||||
|
||||
const TreeSupportSettings config{ m_layer_outlines[m_current_outline_idx].first };
|
||||
m_current_min_xy_dist = config.xy_min_distance;
|
||||
m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist;
|
||||
assert(m_current_min_xy_dist_delta >= 0);
|
||||
m_increase_until_radius = config.increase_radius_until_radius;
|
||||
m_radius_0 = config.getRadius(0);
|
||||
|
||||
#if 0
|
||||
for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) {
|
||||
@ -218,7 +154,7 @@ TreeModelVolumes::TreeModelVolumes(
|
||||
#endif
|
||||
}
|
||||
|
||||
void TreeModelVolumes::precalculate(const coord_t max_layer, std::function<void()> throw_on_cancel)
|
||||
void TreeModelVolumes::precalculate(const PrintObject& print_object, const coord_t max_layer, std::function<void()> throw_on_cancel)
|
||||
{
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
m_precalculated = true;
|
||||
@ -226,7 +162,7 @@ void TreeModelVolumes::precalculate(const coord_t max_layer, std::function<void(
|
||||
// Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant.
|
||||
// Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex
|
||||
// like inital layer diameter are only done in once.
|
||||
TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first);
|
||||
TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first, print_object.slicing_parameters());
|
||||
|
||||
{
|
||||
// calculate which radius each layer in the tip may have.
|
||||
@ -330,6 +266,7 @@ void TreeModelVolumes::precalculate(const coord_t max_layer, std::function<void(
|
||||
for (int k = int(j - 1); k >= int(i); -- k) {
|
||||
std::string legend = format("radius-%1%", unscaled<float>(sorted[k].first.first));
|
||||
expolygons_with_attributes.push_back({ union_ex(sorted[k].second), SVG::ExPolygonAttributes(legend, std::string(colors[(k - int(i)) % num_colors]), 1.) });
|
||||
SVG::export_expolygons(debug_out_path("treesupport_cache-%s-%d-%s.svg", name.data(), sorted[i].first.second, legend.c_str()), { expolygons_with_attributes.back() });
|
||||
}
|
||||
// Render the range of per radius collision polygons into a common SVG.
|
||||
SVG::export_expolygons(debug_out_path("treesupport_cache-%s-%d.svg", name.data(), sorted[i].first.second), expolygons_with_attributes);
|
||||
@ -417,8 +354,6 @@ const Polygons& TreeModelVolumes::getAvoidance(const coord_t orig_radius, LayerI
|
||||
|
||||
const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, LayerIndex layer_idx, std::function<void()> throw_on_cancel) const
|
||||
{
|
||||
if (orig_radius == 0)
|
||||
return this->getCollision(0, layer_idx, true);
|
||||
|
||||
const coord_t radius = ceilRadius(orig_radius);
|
||||
if (std::optional<std::reference_wrapper<const Polygons>> result = m_placeable_areas_cache.getArea({ radius, layer_idx }); result)
|
||||
@ -426,7 +361,10 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L
|
||||
if (m_precalculated) {
|
||||
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
|
||||
tree_supports_show_error("Not precalculated Placeable areas requested."sv, false);
|
||||
}
|
||||
}
|
||||
if (orig_radius == 0)
|
||||
// Placable areas for radius 0 are calculated in the general collision code.
|
||||
return this->getCollision(0, layer_idx, true);
|
||||
const_cast<TreeModelVolumes*>(this)->calculatePlaceables(radius, layer_idx, throw_on_cancel);
|
||||
return getPlaceableAreas(orig_radius, layer_idx, throw_on_cancel);
|
||||
}
|
||||
@ -481,22 +419,20 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||
std::sort(layer_outline_indices.begin(), layer_outline_indices.end(),
|
||||
[this](size_t i, size_t j) { return m_layer_outlines[i].second.size() < m_layer_outlines[j].second.size(); });
|
||||
|
||||
const LayerIndex min_layer_last = m_collision_cache.getMaxCalculatedLayer(radius);
|
||||
std::vector<Polygons> data(max_layer_idx + 1 - min_layer_last, Polygons{});
|
||||
LayerPolygonCache data;
|
||||
data.allocate(m_collision_cache.getMaxCalculatedLayer(radius) + 1, max_layer_idx + 1);
|
||||
|
||||
const bool calculate_placable = m_support_rests_on_model && radius == 0;
|
||||
std::vector<Polygons> data_placeable;
|
||||
LayerPolygonCache data_placeable;
|
||||
if (calculate_placable)
|
||||
data_placeable = std::vector<Polygons>(max_layer_idx + 1 - min_layer_last, Polygons{});
|
||||
data_placeable.allocate(data.begin(), data.end());
|
||||
|
||||
for (size_t outline_idx : layer_outline_indices)
|
||||
if (const std::vector<Polygons> &outlines = m_layer_outlines[outline_idx].second; ! outlines.empty()) {
|
||||
const TreeSupportMeshGroupSettings &settings = m_layer_outlines[outline_idx].first;
|
||||
const coord_t layer_height = settings.layer_height;
|
||||
const coord_t z_distance_bottom = settings.support_bottom_distance;
|
||||
const int z_distance_bottom_layers = round_up_divide<int>(z_distance_bottom, layer_height);
|
||||
const int z_distance_top_layers = round_up_divide<int>(settings.support_top_distance, layer_height);
|
||||
const LayerIndex max_required_layer = std::min<LayerIndex>(outlines.size(), max_layer_idx + std::max(coord_t(1), z_distance_top_layers));
|
||||
const LayerIndex min_layer_bottom = std::max<LayerIndex>(0, min_layer_last - int(z_distance_bottom_layers));
|
||||
const int z_distance_bottom_layers = int(round(double(settings.support_bottom_distance) / double(layer_height)));
|
||||
const int z_distance_top_layers = int(round(double(settings.support_top_distance) / double(layer_height)));
|
||||
const coord_t xy_distance = outline_idx == m_current_outline_idx ? m_current_min_xy_dist :
|
||||
// technically this causes collision for the normal xy_distance to be larger by m_current_min_xy_dist_delta for all
|
||||
// not currently processing meshes as this delta will be added at request time.
|
||||
@ -507,41 +443,84 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||
settings.support_xy_distance;
|
||||
|
||||
// 1) Calculate offsets of collision areas in parallel.
|
||||
std::vector<Polygons> collision_areas_offsetted(max_required_layer + 1 - min_layer_bottom);
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(min_layer_bottom, max_required_layer + 1),
|
||||
[&outlines, &machine_border = m_machine_border, offset_value = radius + xy_distance, min_layer_bottom, &collision_areas_offsetted, &throw_on_cancel]
|
||||
LayerPolygonCache collision_areas_offsetted;
|
||||
collision_areas_offsetted.allocate(
|
||||
std::max<LayerIndex>(0, data.begin() - z_distance_bottom_layers),
|
||||
std::min<LayerIndex>(outlines.size(), data.end() + z_distance_top_layers));
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(collision_areas_offsetted.begin(), collision_areas_offsetted.end()),
|
||||
[&outlines, &machine_border = std::as_const(m_machine_border), offset_value = radius + xy_distance, &collision_areas_offsetted, &throw_on_cancel]
|
||||
(const tbb::blocked_range<LayerIndex> &range) {
|
||||
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
|
||||
Polygons collision_areas = machine_border;
|
||||
append(collision_areas, outlines[layer_idx]);
|
||||
// jtRound is not needed here, as the overshoot can not cause errors in the algorithm, because no assumptions are made about the model.
|
||||
// if a key does not exist when it is accessed it is added!
|
||||
collision_areas_offsetted[layer_idx - min_layer_bottom] = offset_value == 0 ? union_(collision_areas) : offset(union_ex(collision_areas), offset_value, ClipperLib::jtMiter, 1.2);
|
||||
if(throw_on_cancel)
|
||||
collision_areas_offsetted[layer_idx] = offset_value == 0 ?
|
||||
union_(collision_areas) :
|
||||
offset(union_ex(collision_areas), offset_value, ClipperLib::jtMiter, 1.2);
|
||||
throw_on_cancel();
|
||||
}
|
||||
});
|
||||
|
||||
// 2) Sum over top / bottom ranges.
|
||||
const bool last = outline_idx == layer_outline_indices.size();
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(min_layer_last + 1, max_layer_idx + 1),
|
||||
[&collision_areas_offsetted, &anti_overhang = m_anti_overhang, min_layer_bottom, radius, z_distance_bottom_layers, z_distance_top_layers, min_resolution = m_min_resolution, &data, min_layer_last, last, &throw_on_cancel]
|
||||
const bool processing_last_mesh = outline_idx == layer_outline_indices.size();
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(data.begin(), data.end()),
|
||||
[&collision_areas_offsetted, &outlines, &machine_border = m_machine_border, &anti_overhang = m_anti_overhang, radius,
|
||||
xy_distance, z_distance_bottom_layers, z_distance_top_layers, min_resolution = m_min_resolution, &data, processing_last_mesh, &throw_on_cancel]
|
||||
(const tbb::blocked_range<LayerIndex>& range) {
|
||||
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++layer_idx) {
|
||||
Polygons collisions;
|
||||
for (int i = -z_distance_bottom_layers; i <= z_distance_top_layers; ++ i) {
|
||||
int j = layer_idx + i - min_layer_bottom;
|
||||
if (j >= 0 && j < int(collision_areas_offsetted.size()))
|
||||
for (int i = - z_distance_bottom_layers; i <= 0; ++ i)
|
||||
if (int j = layer_idx + i; collision_areas_offsetted.has(j))
|
||||
append(collisions, collision_areas_offsetted[j]);
|
||||
for (int i = 1; i <= z_distance_top_layers; ++ i)
|
||||
if (int j = layer_idx + i; j < int(outlines.size())) {
|
||||
Polygons collision_areas_original = machine_border;
|
||||
append(collision_areas_original, outlines[j]);
|
||||
|
||||
// If just the collision (including the xy distance) of the layers above is accumulated, it leads to the
|
||||
// following issue:
|
||||
// Example: assuming the z distance is 2 layer
|
||||
// + = xy_distance
|
||||
// - = model
|
||||
// o = overhang of the area two layers above that should result in tips on this layer
|
||||
//
|
||||
// +-----+
|
||||
// +-----+
|
||||
// +-----+
|
||||
// o +-----+
|
||||
// If just the collision above is accumulated the overhang will get overwritten by the xy_distance of the
|
||||
// layer below the overhang...
|
||||
//
|
||||
// This only causes issues if the overhang area is thinner than xy_distance
|
||||
// Just accumulating areas of the model above without the xy distance is also problematic, as then support
|
||||
// may get closer to the model (on the diagonal downwards) than the user intended. Example (s = support):
|
||||
// +-----+
|
||||
// +-----+
|
||||
// +-----+
|
||||
// s+-----+
|
||||
|
||||
// technically the calculation below is off by one layer, as the actual distance between plastic one layer
|
||||
// down is 0 not layer height, as this layer is filled with said plastic. But otherwise a part of the
|
||||
// overhang that is expected to be supported is overwritten by the remaining part of the xy distance of the
|
||||
// layer below the to be supported area.
|
||||
coord_t required_range_x =
|
||||
(xy_distance - ((i - (z_distance_top_layers == 1 ? 0.5 : 0)) * xy_distance / z_distance_top_layers));
|
||||
// the conditional -0.5 ensures that plastic can never touch on the diagonal
|
||||
// downward when the z_distance_top_layers = 1. It is assumed to be better to
|
||||
// not support an overhang<90 degree than to risk fusing to it.
|
||||
append(collisions, offset(union_ex(collision_areas_original), radius + required_range_x, ClipperLib::jtMiter, 1.2));
|
||||
}
|
||||
collisions = last && layer_idx < int(anti_overhang.size()) ? union_(collisions, offset(union_ex(anti_overhang[layer_idx]), radius, ClipperLib::jtMiter, 1.2)) : union_(collisions);
|
||||
auto &dst = data[layer_idx - (min_layer_last + 1)];
|
||||
if (last) {
|
||||
collisions = processing_last_mesh && layer_idx < int(anti_overhang.size()) ?
|
||||
union_(collisions, offset(union_ex(anti_overhang[layer_idx]), radius, ClipperLib::jtMiter, 1.2)) :
|
||||
union_(collisions);
|
||||
auto &dst = data[layer_idx];
|
||||
if (processing_last_mesh) {
|
||||
if (! dst.empty())
|
||||
collisions = union_(collisions, dst);
|
||||
dst = polygons_simplify(collisions, min_resolution);
|
||||
} else
|
||||
append(dst, collisions);
|
||||
append(dst, std::move(collisions));
|
||||
if (throw_on_cancel)
|
||||
throw_on_cancel();
|
||||
}
|
||||
@ -550,17 +529,21 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||
// 3) Optionally calculate placables.
|
||||
if (calculate_placable) {
|
||||
// Calculating both the collision areas and placable areas.
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(min_layer_last + 1, z_distance_bottom_layers + 1), max_layer_idx + 1),
|
||||
[&collision_areas_offsetted, &anti_overhang = m_anti_overhang, min_layer_bottom, z_distance_bottom_layers, last, min_resolution = m_min_resolution, &data_placeable, min_layer_last, &throw_on_cancel]
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(z_distance_bottom_layers + 1, data.begin()), data.end()),
|
||||
[&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh,
|
||||
min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel]
|
||||
(const tbb::blocked_range<LayerIndex>& range) {
|
||||
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
|
||||
LayerIndex layer_idx_below = layer_idx - (z_distance_bottom_layers + 1) - min_layer_bottom;
|
||||
LayerIndex layer_idx_below = layer_idx - z_distance_bottom_layers - 1;
|
||||
assert(layer_idx_below >= 0);
|
||||
auto ¤t = collision_areas_offsetted[layer_idx - min_layer_bottom];
|
||||
auto &below = collision_areas_offsetted[layer_idx_below];
|
||||
auto placable = diff(below, layer_idx < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx - (z_distance_bottom_layers + 1)]) : current);
|
||||
auto &dst = data_placeable[layer_idx - (min_layer_last + 1)];
|
||||
if (last) {
|
||||
const Polygons ¤t = collision_areas_offsetted[layer_idx];
|
||||
const Polygons &below = outlines[layer_idx_below];
|
||||
Polygons placable = diff(
|
||||
// Inflate the surface to sit on by the separation distance to increase chance of a support being placed on a sloped surface.
|
||||
offset(below, xy_distance),
|
||||
layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current);
|
||||
auto &dst = data_placeable[layer_idx];
|
||||
if (processing_last_mesh) {
|
||||
if (! dst.empty())
|
||||
placable = union_(placable, dst);
|
||||
dst = polygons_simplify(placable, min_resolution);
|
||||
@ -585,9 +568,9 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||
#endif
|
||||
if (throw_on_cancel)
|
||||
throw_on_cancel();
|
||||
m_collision_cache.insert(std::move(data), min_layer_last + 1, radius);
|
||||
m_collision_cache.insert(std::move(data), radius);
|
||||
if (calculate_placable)
|
||||
m_placeable_areas_cache.insert(std::move(data_placeable), min_layer_last + 1, radius);
|
||||
m_placeable_areas_cache.insert(std::move(data_placeable), radius);
|
||||
}
|
||||
|
||||
void TreeModelVolumes::calculateCollisionHolefree(const std::vector<RadiusLayerPair> &keys, std::function<void()> throw_on_cancel)
|
||||
@ -653,7 +636,8 @@ void TreeModelVolumes::calculateAvoidance(const std::vector<RadiusLayerPair> &ke
|
||||
BOOST_LOG_TRIVIAL(debug) << "Calculation requested for value already calculated?";
|
||||
continue;
|
||||
}
|
||||
if (! task.holefree() || task.radius < m_increase_until_radius + m_current_min_xy_dist_delta)
|
||||
if ((task.to_model ? to_model : to_build_plate) &&
|
||||
(! task.holefree() || task.radius < m_increase_until_radius + m_current_min_xy_dist_delta))
|
||||
avoidance_tasks.emplace_back(task);
|
||||
}
|
||||
|
||||
@ -679,7 +663,7 @@ void TreeModelVolumes::calculateAvoidance(const std::vector<RadiusLayerPair> &ke
|
||||
assert(move_steps > 0);
|
||||
float last_move_step = max_move - (move_steps - 1) * move_step;
|
||||
if (last_move_step < scaled<float>(0.05)) {
|
||||
assert(move_steps > 1);
|
||||
//assert(move_steps > 1);
|
||||
if (move_steps > 1) {
|
||||
// Avoid taking a very short last step, stretch the other steps a bit instead.
|
||||
move_step = max_move / (-- move_steps);
|
@ -14,9 +14,11 @@
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include "Point.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "TreeSupportCommon.hpp"
|
||||
|
||||
#include "../Point.hpp"
|
||||
#include "../Polygon.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
@ -27,169 +29,10 @@ class PrintObject;
|
||||
namespace TreeSupport3D
|
||||
{
|
||||
|
||||
using LayerIndex = int;
|
||||
|
||||
struct TreeSupportMeshGroupSettings {
|
||||
TreeSupportMeshGroupSettings() = default;
|
||||
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object);
|
||||
|
||||
/*********************************************************************/
|
||||
/* Print parameters, not support specific: */
|
||||
/*********************************************************************/
|
||||
coord_t layer_height { scaled<coord_t>(0.15) };
|
||||
// Maximum Deviation (meshfix_maximum_deviation)
|
||||
// The maximum deviation allowed when reducing the resolution for the Maximum Resolution setting. If you increase this,
|
||||
// the print will be less accurate, but the g-code will be smaller. Maximum Deviation is a limit for Maximum Resolution,
|
||||
// so if the two conflict the Maximum Deviation will always be held true.
|
||||
coord_t resolution { scaled<coord_t>(0.025) };
|
||||
// Minimum Feature Size (aka minimum line width) - Arachne specific
|
||||
// Minimum thickness of thin features. Model features that are thinner than this value will not be printed, while features thicker
|
||||
// than the Minimum Feature Size will be widened to the Minimum Wall Line Width.
|
||||
coord_t min_feature_size { scaled<coord_t>(0.1) };
|
||||
|
||||
/*********************************************************************/
|
||||
/* General support parameters: */
|
||||
/*********************************************************************/
|
||||
|
||||
// Support Overhang Angle
|
||||
// The minimum angle of overhangs for which support is added. At a value of 0° all overhangs are supported, 90° will not provide any support.
|
||||
double support_angle { 50. * M_PI / 180. };
|
||||
// Support Line Width
|
||||
// Width of a single support structure line.
|
||||
coord_t support_line_width { scaled<coord_t>(0.4) };
|
||||
// Support Roof Line Width: Width of a single support roof line.
|
||||
coord_t support_roof_line_width { scaled<coord_t>(0.4) };
|
||||
// Enable Support Floor (aka bottom interfaces)
|
||||
// Generate a dense slab of material between the bottom of the support and the model. This will create a skin between the model and support.
|
||||
bool support_bottom_enable { false };
|
||||
// Support Floor Thickness
|
||||
// The thickness of the support floors. This controls the number of dense layers that are printed on top of places of a model on which support rests.
|
||||
coord_t support_bottom_height { scaled<coord_t>(1.) };
|
||||
bool support_material_buildplate_only { false };
|
||||
// Support X/Y Distance
|
||||
// Distance of the support structure from the print in the X/Y directions.
|
||||
// minimum: 0, maximum warning: 1.5 * machine_nozzle_tip_outer_diameter
|
||||
coord_t support_xy_distance { scaled<coord_t>(0.7) };
|
||||
// Minimum Support X/Y Distance
|
||||
// Distance of the support structure from the overhang in the X/Y directions.
|
||||
// minimum_value: 0, minimum warning": support_xy_distance - support_line_width * 2, maximum warning: support_xy_distance
|
||||
coord_t support_xy_distance_overhang { scaled<coord_t>(0.2) };
|
||||
// Support Top Distance
|
||||
// Distance from the top of the support to the print.
|
||||
coord_t support_top_distance { scaled<coord_t>(0.1) };
|
||||
// Support Bottom Distance
|
||||
// Distance from the print to the bottom of the support.
|
||||
coord_t support_bottom_distance { scaled<coord_t>(0.1) };
|
||||
//FIXME likely not needed, optimization for clipping of interface layers
|
||||
// When checking where there's model above and below the support, take steps of the given height. Lower values will slice slower, while higher values
|
||||
// may cause normal support to be printed in some places where there should have been support interface.
|
||||
coord_t support_interface_skip_height { scaled<coord_t>(0.3) };
|
||||
// Support Infill Line Directions
|
||||
// A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end
|
||||
// of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained
|
||||
// in square brackets. Default is an empty list which means use the default angle 0 degrees.
|
||||
// std::vector<double> support_infill_angles {};
|
||||
// Enable Support Roof
|
||||
// Generate a dense slab of material between the top of support and the model. This will create a skin between the model and support.
|
||||
bool support_roof_enable { false };
|
||||
// Support Roof Thickness
|
||||
// The thickness of the support roofs. This controls the amount of dense layers at the top of the support on which the model rests.
|
||||
coord_t support_roof_height { scaled<coord_t>(1.) };
|
||||
// Minimum Support Roof Area
|
||||
// Minimum area size for the roofs of the support. Polygons which have an area smaller than this value will be printed as normal support.
|
||||
double minimum_roof_area { scaled<double>(scaled<double>(1.)) };
|
||||
// A list of integer line directions to use. Elements from the list are used sequentially as the layers progress
|
||||
// and when the end of the list is reached, it starts at the beginning again. The list items are separated
|
||||
// by commas and the whole list is contained in square brackets. Default is an empty list which means
|
||||
// use the default angles (alternates between 45 and 135 degrees if interfaces are quite thick or 90 degrees).
|
||||
std::vector<double> support_roof_angles {};
|
||||
// Support Roof Pattern (aka top interface)
|
||||
// The pattern with which the roofs of the support are printed.
|
||||
SupportMaterialInterfacePattern support_roof_pattern { smipAuto };
|
||||
// Support Pattern
|
||||
// The pattern of the support structures of the print. The different options available result in sturdy or easy to remove support.
|
||||
SupportMaterialPattern support_pattern { smpRectilinear };
|
||||
// Support Line Distance
|
||||
// Distance between the printed support structure lines. This setting is calculated by the support density.
|
||||
coord_t support_line_spacing { scaled<coord_t>(2.66 - 0.4) };
|
||||
// Support Floor Horizontal Expansion
|
||||
// Amount of offset applied to the floors of the support.
|
||||
coord_t support_bottom_offset { scaled<coord_t>(0.) };
|
||||
// Support Wall Line Count
|
||||
// The number of walls with which to surround support infill. Adding a wall can make support print more reliably
|
||||
// and can support overhangs better, but increases print time and material used.
|
||||
// tree: 1, zig-zag: 0, concentric: 1
|
||||
int support_wall_count { 1 };
|
||||
// Support Roof Line Distance
|
||||
// Distance between the printed support roof lines. This setting is calculated by the Support Roof Density, but can be adjusted separately.
|
||||
coord_t support_roof_line_distance { scaled<coord_t>(0.4) };
|
||||
// Minimum Support Area
|
||||
// Minimum area size for support polygons. Polygons which have an area smaller than this value will not be generated.
|
||||
coord_t minimum_support_area { scaled<coord_t>(0.) };
|
||||
// Minimum Support Floor Area
|
||||
// Minimum area size for the floors of the support. Polygons which have an area smaller than this value will be printed as normal support.
|
||||
coord_t minimum_bottom_area { scaled<coord_t>(1.0) };
|
||||
// Support Horizontal Expansion
|
||||
// Amount of offset applied to all support polygons in each layer. Positive values can smooth out the support areas and result in more sturdy support.
|
||||
coord_t support_offset { scaled<coord_t>(0.) };
|
||||
|
||||
/*********************************************************************/
|
||||
/* Parameters for the Cura tree supports implementation: */
|
||||
/*********************************************************************/
|
||||
|
||||
// Tree Support Maximum Branch Angle
|
||||
// The maximum angle of the branches, when the branches have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach.
|
||||
// minimum: 0, minimum warning: 20, maximum: 89, maximum warning": 85
|
||||
double support_tree_angle { 60. * M_PI / 180. };
|
||||
// Tree Support Branch Diameter Angle
|
||||
// The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length.
|
||||
// A bit of an angle can increase stability of the tree support.
|
||||
// minimum: 0, maximum: 89.9999, maximum warning: 15
|
||||
double support_tree_branch_diameter_angle { 5. * M_PI / 180. };
|
||||
// Tree Support Branch Distance
|
||||
// How far apart the branches need to be when they touch the model. Making this distance small will cause
|
||||
// the tree support to touch the model at more points, causing better overhang but making support harder to remove.
|
||||
coord_t support_tree_branch_distance { scaled<coord_t>(1.) };
|
||||
// Tree Support Branch Diameter
|
||||
// The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this.
|
||||
// minimum: 0.001, minimum warning: support_line_width * 2
|
||||
coord_t support_tree_branch_diameter { scaled<coord_t>(2.) };
|
||||
|
||||
/*********************************************************************/
|
||||
/* Parameters new to the Thomas Rahm's tree supports implementation: */
|
||||
/*********************************************************************/
|
||||
|
||||
// Tree Support Preferred Branch Angle
|
||||
// The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster.
|
||||
// minimum: 0, minimum warning: 10, maximum: support_tree_angle, maximum warning: support_tree_angle-1
|
||||
double support_tree_angle_slow { 50. * M_PI / 180. };
|
||||
// Tree Support Diameter Increase To Model
|
||||
// The most the diameter of a branch that has to connect to the model may increase by merging with branches that could reach the buildplate.
|
||||
// Increasing this reduces print time, but increases the area of support that rests on model
|
||||
// minimum: 0
|
||||
coord_t support_tree_max_diameter_increase_by_merges_when_support_to_model { scaled<coord_t>(1.0) };
|
||||
// Tree Support Minimum Height To Model
|
||||
// How tall a branch has to be if it is placed on the model. Prevents small blobs of support. This setting is ignored when a branch is supporting a support roof.
|
||||
// minimum: 0, maximum warning: 5
|
||||
coord_t support_tree_min_height_to_model { scaled<coord_t>(1.0) };
|
||||
// Tree Support Inital Layer Diameter
|
||||
// Diameter every branch tries to achieve when reaching the buildplate. Improves bed adhesion.
|
||||
// minimum: 0, maximum warning: 20
|
||||
coord_t support_tree_bp_diameter { scaled<coord_t>(7.5) };
|
||||
// Tree Support Branch Density
|
||||
// Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs,
|
||||
// but the supports are harder to remove. Use Support Roof for very high values or ensure support density is similarly high at the top.
|
||||
// 5%-35%
|
||||
double support_tree_top_rate { 15. };
|
||||
// Tree Support Tip Diameter
|
||||
// The diameter of the top of the tip of the branches of tree support.
|
||||
// minimum: min_wall_line_width, minimum warning: min_wall_line_width+0.05, maximum_value: support_tree_branch_diameter, value: support_line_width
|
||||
coord_t support_tree_tip_diameter { scaled<coord_t>(0.4) };
|
||||
|
||||
// Support Interface Priority
|
||||
// How support interface and support will interact when they overlap. Currently only implemented for support roof.
|
||||
//enum support_interface_priority { support_lines_overwrite_interface_area };
|
||||
};
|
||||
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
|
||||
static constexpr const coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled<coord_t>(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR);
|
||||
static constexpr const coord_t SUPPORT_TREE_COLLISION_RESOLUTION = scaled<coord_t>(0.5);
|
||||
static constexpr const bool SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true;
|
||||
|
||||
class TreeModelVolumes
|
||||
{
|
||||
@ -240,7 +83,7 @@ public:
|
||||
* Knowledge about branch angle is used to only calculate avoidances and collisions that may actually be needed.
|
||||
* Not calling precalculate() will cause the class to lazily calculate avoidances and collisions as needed, which will be a lot slower on systems with more then one or two cores!
|
||||
*/
|
||||
void precalculate(const coord_t max_layer, std::function<void()> throw_on_cancel);
|
||||
void precalculate(const PrintObject& print_object, const coord_t max_layer, std::function<void()> throw_on_cancel);
|
||||
|
||||
/*!
|
||||
* \brief Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer.
|
||||
@ -326,6 +169,29 @@ public:
|
||||
Polygon m_bed_area;
|
||||
|
||||
private:
|
||||
// Caching polygons for a range of layers.
|
||||
class LayerPolygonCache {
|
||||
public:
|
||||
void allocate(LayerIndex aidx_begin, LayerIndex aidx_end) {
|
||||
m_idx_begin = aidx_begin;
|
||||
m_idx_end = aidx_end;
|
||||
m_polygons.assign(aidx_end - aidx_begin, {});
|
||||
}
|
||||
|
||||
LayerIndex begin() const { return m_idx_begin; }
|
||||
LayerIndex end() const { return m_idx_end; }
|
||||
size_t size() const { return m_polygons.size(); }
|
||||
|
||||
bool has(LayerIndex idx) const { return idx >= m_idx_begin && idx < m_idx_end; }
|
||||
Polygons& operator[](LayerIndex idx) { assert(idx >= m_idx_begin && idx < m_idx_end); return m_polygons[idx - m_idx_begin]; }
|
||||
std::vector<Polygons>& polygons_mutable() { return m_polygons; }
|
||||
|
||||
private:
|
||||
std::vector<Polygons> m_polygons;
|
||||
LayerIndex m_idx_begin;
|
||||
LayerIndex m_idx_end;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Convenience typedef for the keys to the caches
|
||||
*/
|
||||
@ -361,6 +227,13 @@ private:
|
||||
for (auto &d : in)
|
||||
m_data[first_layer_idx ++].emplace(radius, std::move(d));
|
||||
}
|
||||
void insert(LayerPolygonCache &&in, coord_t radius) {
|
||||
std::lock_guard<std::mutex> guard(m_mutex);
|
||||
LayerIndex i = in.begin();
|
||||
allocate_layers(i + LayerIndex(in.size()));
|
||||
for (auto &d : in.polygons_mutable())
|
||||
m_data[i ++].emplace(radius, std::move(d));
|
||||
}
|
||||
/*!
|
||||
* \brief Checks a cache for a given RadiusLayerPair and returns it if it is found
|
||||
* \param key RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer.
|
||||
@ -616,6 +489,9 @@ private:
|
||||
*/
|
||||
coord_t m_radius_0;
|
||||
|
||||
// Z heights of the raft layers (additional layers below the object, last raft layer aligned with the bottom of the first object layer).
|
||||
std::vector<double> m_raft_layers;
|
||||
|
||||
/*!
|
||||
* \brief Caches for the collision, avoidance and areas on the model where support can be placed safely
|
||||
* at given radius and layer indices.
|
@ -3,14 +3,14 @@
|
||||
|
||||
#include <forward_list>
|
||||
#include <unordered_set>
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include "MinimumSpanningTree.hpp"
|
||||
#include "tbb/concurrent_unordered_map.h"
|
||||
#include "Flow.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Fill/Lightning/Generator.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../Point.hpp"
|
||||
#include "../Slicing.hpp"
|
||||
#include "../MinimumSpanningTree.hpp"
|
||||
#include "../Flow.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Fill/Lightning/Generator.hpp"
|
||||
#include "TreeModelVolumes.hpp"
|
||||
#include "TreeSupport3D.hpp"
|
||||
|
File diff suppressed because it is too large
Load Diff
332
src/libslic3r/Support/TreeSupport3D.hpp
Normal file
332
src/libslic3r/Support/TreeSupport3D.hpp
Normal file
@ -0,0 +1,332 @@
|
||||
// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine.
|
||||
// Original source of Thomas Rahm's tree supports:
|
||||
// https://github.com/ThomasRahm/CuraEngine
|
||||
//
|
||||
// Original CuraEngine copyright:
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#ifndef slic3r_TreeSupport_hpp
|
||||
#define slic3r_TreeSupport_hpp
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include "../Point.hpp"
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "TreeModelVolumes.hpp"
|
||||
#include "TreeSupportCommon.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
|
||||
{
|
||||
|
||||
// Forward declarations
|
||||
class TreeSupport;
|
||||
class Print;
|
||||
class PrintObject;
|
||||
class SupportGeneratorLayer;
|
||||
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
|
||||
|
||||
namespace TreeSupport3D
|
||||
{
|
||||
|
||||
// The number of vertices in each circle.
|
||||
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
|
||||
|
||||
struct AreaIncreaseSettings
|
||||
{
|
||||
AreaIncreaseSettings(
|
||||
TreeModelVolumes::AvoidanceType type = TreeModelVolumes::AvoidanceType::Fast, coord_t increase_speed = 0,
|
||||
bool increase_radius = false, bool no_error = false, bool use_min_distance = false, bool move = false) :
|
||||
increase_speed{ increase_speed }, type{ type }, increase_radius{ increase_radius }, no_error{ no_error }, use_min_distance{ use_min_distance }, move{ move } {}
|
||||
|
||||
coord_t increase_speed;
|
||||
// Packing for smaller memory footprint of SupportElementState && SupportElementMerging
|
||||
TreeModelVolumes::AvoidanceType type;
|
||||
bool increase_radius : 1;
|
||||
bool no_error : 1;
|
||||
bool use_min_distance : 1;
|
||||
bool move : 1;
|
||||
bool operator==(const AreaIncreaseSettings& other) const
|
||||
{
|
||||
return type == other.type &&
|
||||
increase_speed == other.increase_speed &&
|
||||
increase_radius == other.increase_radius &&
|
||||
no_error == other.no_error &&
|
||||
use_min_distance == other.use_min_distance &&
|
||||
move == other.move;
|
||||
}
|
||||
};
|
||||
|
||||
#define TREE_SUPPORTS_TRACK_LOST
|
||||
|
||||
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
|
||||
struct SupportElementStateBits {
|
||||
SupportElementStateBits() :
|
||||
to_buildplate(false),
|
||||
to_model_gracious(false),
|
||||
use_min_xy_dist(false),
|
||||
supports_roof(false),
|
||||
can_use_safe_radius(false),
|
||||
skip_ovalisation(false),
|
||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
||||
lost(false),
|
||||
verylost(false),
|
||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
||||
deleted(false),
|
||||
marked(false)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* \brief The element trys to reach the buildplate
|
||||
*/
|
||||
bool to_buildplate : 1;
|
||||
|
||||
/*!
|
||||
* \brief Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
|
||||
*/
|
||||
bool to_model_gracious : 1;
|
||||
|
||||
/*!
|
||||
* \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.
|
||||
*/
|
||||
bool use_min_xy_dist : 1;
|
||||
|
||||
/*!
|
||||
* \brief True if this Element or any parent provides support to a support roof.
|
||||
*/
|
||||
bool supports_roof : 1;
|
||||
|
||||
/*!
|
||||
* \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.
|
||||
*/
|
||||
bool can_use_safe_radius : 1;
|
||||
|
||||
/*!
|
||||
* \brief Skip the ovalisation to parent and children when generating the final circles.
|
||||
*/
|
||||
bool skip_ovalisation : 1;
|
||||
|
||||
#ifdef TREE_SUPPORTS_TRACK_LOST
|
||||
// Likely a lost branch, debugging information.
|
||||
bool lost : 1;
|
||||
bool verylost : 1;
|
||||
#endif // TREE_SUPPORTS_TRACK_LOST
|
||||
|
||||
// Not valid anymore, to be deleted.
|
||||
bool deleted : 1;
|
||||
|
||||
// General purpose flag marking a visited element.
|
||||
bool marked : 1;
|
||||
};
|
||||
|
||||
struct SupportElementState : public SupportElementStateBits
|
||||
{
|
||||
int type = 0;
|
||||
coordf_t radius = 0;
|
||||
float print_z = 0;
|
||||
|
||||
/*!
|
||||
* \brief The layer this support elements wants reach
|
||||
*/
|
||||
LayerIndex target_height;
|
||||
|
||||
/*!
|
||||
* \brief The position this support elements wants to support on layer=target_height
|
||||
*/
|
||||
Point target_position;
|
||||
|
||||
/*!
|
||||
* \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 trys not to move until this dtt is reached, is set to 0 if the element had to move.
|
||||
*/
|
||||
uint32_t dont_move_until;
|
||||
|
||||
/*!
|
||||
* \brief Settings used to increase the influence area to its current state.
|
||||
*/
|
||||
AreaIncreaseSettings last_area_increase;
|
||||
|
||||
/*!
|
||||
* \brief Amount of roof layers that were not yet added, because the branch needed to move.
|
||||
*/
|
||||
uint32_t missing_roof_layers;
|
||||
|
||||
// called by increase_single_area() and increaseAreas()
|
||||
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState& src)
|
||||
{
|
||||
SupportElementState dst{ src };
|
||||
++dst.distance_to_top;
|
||||
--dst.layer_idx;
|
||||
// set to invalid as we are a new node on a new layer
|
||||
dst.result_on_layer_reset();
|
||||
dst.skip_ovalisation = false;
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* \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;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the Radius, that this element will have.
|
||||
* \param elem[in] The Element.
|
||||
* \return The radius the element has.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
|
||||
{
|
||||
return settings.getRadius(getEffectiveDTT(settings, elem), elem.elephant_foot_increases);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.
|
||||
* \param elem[in] The Element.
|
||||
* \return The collision radius the element has.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
|
||||
{
|
||||
return settings.getRadius(elem.effective_radius_height, elem.elephant_foot_increases);
|
||||
}
|
||||
|
||||
struct SupportElement
|
||||
{
|
||||
using ParentIndices =
|
||||
#ifdef NDEBUG
|
||||
// To reduce memory allocation in release mode.
|
||||
boost::container::small_vector<int32_t, 4>;
|
||||
#else // NDEBUG
|
||||
// To ease debugging.
|
||||
std::vector<int32_t>;
|
||||
#endif // NDEBUG
|
||||
|
||||
// SupportElement(const SupportElementState &state) : SupportElementState(state) {}
|
||||
SupportElement(const SupportElementState &state, Polygons &&influence_area) : state(state), influence_area(std::move(influence_area)) {}
|
||||
SupportElement(const SupportElementState &state, ParentIndices &&parents, Polygons &&influence_area) :
|
||||
state(state), parents(std::move(parents)), influence_area(std::move(influence_area)) {}
|
||||
|
||||
SupportElementState state;
|
||||
|
||||
/*!
|
||||
* \brief All elements in the layer above the current one that are supported by this element
|
||||
*/
|
||||
ParentIndices parents;
|
||||
|
||||
/*!
|
||||
* \brief The resulting influence area.
|
||||
* Will only be set in the results of createLayerPathing, and will be nullptr inside!
|
||||
*/
|
||||
Polygons influence_area;
|
||||
};
|
||||
|
||||
void tree_supports_show_error(std::string_view message, bool critical);
|
||||
|
||||
using SupportElements = std::deque<SupportElement>;
|
||||
|
||||
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
|
||||
{
|
||||
return support_element_radius(settings, elem.state);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElement &elem)
|
||||
{
|
||||
return support_element_collision_radius(settings, elem.state);
|
||||
}
|
||||
|
||||
void create_layer_pathing(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
|
||||
|
||||
void create_nodes_from_area(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
|
||||
|
||||
void organic_smooth_branches_avoid_collisions(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<std::pair<SupportElement*, int>>& elements_with_link_down, const std::vector<size_t>& linear_data_layers, std::function<void()> throw_on_cancel);
|
||||
|
||||
indexed_triangle_set draw_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
|
||||
|
||||
void slice_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, const indexed_triangle_set& cummulative_mesh, SupportGeneratorLayersPtr& bottom_contacts, SupportGeneratorLayersPtr& top_contacts, SupportGeneratorLayersPtr& intermediate_layers, SupportGeneratorLayerStorage& layer_storage, std::function<void()> throw_on_cancel);
|
||||
|
||||
void generate_initial_areas(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, InterfacePlacer& interface_placer, std::function<void()> throw_on_cancel);
|
||||
|
||||
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
|
||||
void organic_draw_branches(
|
||||
PrintObject& print_object,
|
||||
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 TreeSupport3D
|
||||
|
||||
void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_support, std::function<void()> throw_on_cancel = []{});
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_TreeSupport_hpp */
|
730
src/libslic3r/Support/TreeSupportCommon.hpp
Normal file
730
src/libslic3r/Support/TreeSupportCommon.hpp
Normal file
@ -0,0 +1,730 @@
|
||||
#pragma once
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include "../Point.hpp"
|
||||
#include "../libslic3r.h"
|
||||
#include "../Print.hpp"
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "../Slicing.hpp" // SlicingParams
|
||||
#include "TreeModelVolumes.hpp"
|
||||
#include "SupportLayer.hpp"
|
||||
#include "SupportParameters.hpp"
|
||||
namespace Slic3r
|
||||
{
|
||||
namespace TreeSupport3D
|
||||
{
|
||||
using LayerIndex = int;
|
||||
|
||||
enum class InterfacePreference
|
||||
{
|
||||
InterfaceAreaOverwritesSupport,
|
||||
SupportAreaOverwritesInterface,
|
||||
InterfaceLinesOverwriteSupport,
|
||||
SupportLinesOverwriteInterface,
|
||||
Nothing
|
||||
};
|
||||
|
||||
struct TreeSupportMeshGroupSettings {
|
||||
TreeSupportMeshGroupSettings() = default;
|
||||
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.support_material);
|
||||
//assert(config.support_material_style == smsTree || config.support_material_style == smsOrganic);
|
||||
|
||||
// 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_roof_enable = config.support_interface_top_layers.value > 0;
|
||||
this->support_roof_layers = config.support_interface_top_layers.value;
|
||||
this->support_floor_enable = config.support_interface_bottom_layers.value > 0;
|
||||
this->support_floor_layers = config.support_interface_bottom_layers.value;
|
||||
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_wall_count = std::max(1, config.tree_support_wall_count.value); // at least 1 wall for organic tree support
|
||||
this->support_roof_line_distance = scaled<coord_t>(config.support_interface_spacing.value) + this->support_roof_line_width;
|
||||
double support_tree_angle_slow = 25;// TODO add a setting?
|
||||
double support_tree_branch_diameter_angle = 5; // TODO add a setting?
|
||||
double tree_support_tip_diameter = 0.8;
|
||||
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_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter.value);
|
||||
this->support_tree_branch_diameter_angle = std::clamp<double>(support_tree_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
|
||||
this->support_tree_top_rate = 30; // percent
|
||||
// this->support_tree_tip_diameter = this->support_line_width;
|
||||
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(tree_support_tip_diameter), 0, this->support_tree_branch_diameter);
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
/* Print parameters, not support specific: */
|
||||
/*********************************************************************/
|
||||
coord_t layer_height { scaled<coord_t>(0.15) };
|
||||
// Maximum Deviation (meshfix_maximum_deviation)
|
||||
// The maximum deviation allowed when reducing the resolution for the Maximum Resolution setting. If you increase this,
|
||||
// the print will be less accurate, but the g-code will be smaller. Maximum Deviation is a limit for Maximum Resolution,
|
||||
// so if the two conflict the Maximum Deviation will always be held true.
|
||||
coord_t resolution { scaled<coord_t>(0.025) };
|
||||
// Minimum Feature Size (aka minimum line width) - Arachne specific
|
||||
// Minimum thickness of thin features. Model features that are thinner than this value will not be printed, while features thicker
|
||||
// than the Minimum Feature Size will be widened to the Minimum Wall Line Width.
|
||||
coord_t min_feature_size { scaled<coord_t>(0.1) };
|
||||
|
||||
/*********************************************************************/
|
||||
/* General support parameters: */
|
||||
/*********************************************************************/
|
||||
|
||||
// Support Overhang Angle
|
||||
// The minimum angle of overhangs for which support is added. At a value of 0° all overhangs are supported, 90° will not provide any support.
|
||||
double support_angle { 50. * M_PI / 180. };
|
||||
// Support Line Width
|
||||
// Width of a single support structure line.
|
||||
coord_t support_line_width { scaled<coord_t>(0.4) };
|
||||
// Support Roof Line Width: Width of a single support roof line.
|
||||
coord_t support_roof_line_width { scaled<coord_t>(0.4) };
|
||||
// Enable Support Floor (aka bottom interfaces)
|
||||
// Generate a dense slab of material between the bottom of the support and the model. This will create a skin between the model and support.
|
||||
bool support_bottom_enable { false };
|
||||
// Support Floor Thickness
|
||||
// The thickness of the support floors. This controls the number of dense layers that are printed on top of places of a model on which support rests.
|
||||
coord_t support_bottom_height { scaled<coord_t>(1.) };
|
||||
bool support_material_buildplate_only { false };
|
||||
// Support X/Y Distance
|
||||
// Distance of the support structure from the print in the X/Y directions.
|
||||
// minimum: 0, maximum warning: 1.5 * machine_nozzle_tip_outer_diameter
|
||||
coord_t support_xy_distance { scaled<coord_t>(0.7) };
|
||||
// Minimum Support X/Y Distance
|
||||
// Distance of the support structure from the overhang in the X/Y directions.
|
||||
// minimum_value: 0, minimum warning": support_xy_distance - support_line_width * 2, maximum warning: support_xy_distance
|
||||
coord_t support_xy_distance_overhang { scaled<coord_t>(0.2) };
|
||||
// Support Top Distance
|
||||
// Distance from the top of the support to the print.
|
||||
coord_t support_top_distance { scaled<coord_t>(0.1) };
|
||||
// Support Bottom Distance
|
||||
// Distance from the print to the bottom of the support.
|
||||
coord_t support_bottom_distance { scaled<coord_t>(0.1) };
|
||||
//FIXME likely not needed, optimization for clipping of interface layers
|
||||
// When checking where there's model above and below the support, take steps of the given height. Lower values will slice slower, while higher values
|
||||
// may cause normal support to be printed in some places where there should have been support interface.
|
||||
coord_t support_interface_skip_height { scaled<coord_t>(0.3) };
|
||||
// Support Infill Line Directions
|
||||
// A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end
|
||||
// of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained
|
||||
// in square brackets. Default is an empty list which means use the default angle 0 degrees.
|
||||
// std::vector<double> support_infill_angles {};
|
||||
// Enable Support Roof
|
||||
// Generate a dense slab of material between the top of support and the model. This will create a skin between the model and support.
|
||||
bool support_roof_enable { false };
|
||||
// Support Roof Thickness
|
||||
// The thickness of the support roofs. This controls the amount of dense layers at the top of the support on which the model rests.
|
||||
coord_t support_roof_layers{ 2 };
|
||||
bool support_floor_enable{ false };
|
||||
coord_t support_floor_layers{ 2 };
|
||||
|
||||
// Minimum Support Roof Area
|
||||
// Minimum area size for the roofs of the support. Polygons which have an area smaller than this value will be printed as normal support.
|
||||
double minimum_roof_area { scaled<double>(scaled<double>(1.)) };
|
||||
// A list of integer line directions to use. Elements from the list are used sequentially as the layers progress
|
||||
// and when the end of the list is reached, it starts at the beginning again. The list items are separated
|
||||
// by commas and the whole list is contained in square brackets. Default is an empty list which means
|
||||
// use the default angles (alternates between 45 and 135 degrees if interfaces are quite thick or 90 degrees).
|
||||
std::vector<double> support_roof_angles {};
|
||||
// Support Roof Pattern (aka top interface)
|
||||
// The pattern with which the roofs of the support are printed.
|
||||
SupportMaterialInterfacePattern support_roof_pattern { smipAuto };
|
||||
// Support Pattern
|
||||
// The pattern of the support structures of the print. The different options available result in sturdy or easy to remove support.
|
||||
SupportMaterialPattern support_pattern { smpRectilinear };
|
||||
// Support Line Distance
|
||||
// Distance between the printed support structure lines. This setting is calculated by the support density.
|
||||
coord_t support_line_spacing { scaled<coord_t>(2.66 - 0.4) };
|
||||
// Support Floor Horizontal Expansion
|
||||
// Amount of offset applied to the floors of the support.
|
||||
coord_t support_bottom_offset { scaled<coord_t>(0.) };
|
||||
// Support Wall Line Count
|
||||
// The number of walls with which to surround support infill. Adding a wall can make support print more reliably
|
||||
// and can support overhangs better, but increases print time and material used.
|
||||
// tree: 1, zig-zag: 0, concentric: 1
|
||||
int support_wall_count { 1 };
|
||||
// Support Roof Line Distance
|
||||
// Distance between the printed support roof lines. This setting is calculated by the Support Roof Density, but can be adjusted separately.
|
||||
coord_t support_roof_line_distance { scaled<coord_t>(0.4) };
|
||||
// Minimum Support Area
|
||||
// Minimum area size for support polygons. Polygons which have an area smaller than this value will not be generated.
|
||||
coord_t minimum_support_area { scaled<coord_t>(0.) };
|
||||
// Minimum Support Floor Area
|
||||
// Minimum area size for the floors of the support. Polygons which have an area smaller than this value will be printed as normal support.
|
||||
coord_t minimum_bottom_area { scaled<coord_t>(1.0) };
|
||||
// Support Horizontal Expansion
|
||||
// Amount of offset applied to all support polygons in each layer. Positive values can smooth out the support areas and result in more sturdy support.
|
||||
coord_t support_offset { scaled<coord_t>(0.) };
|
||||
|
||||
/*********************************************************************/
|
||||
/* Parameters for the Cura tree supports implementation: */
|
||||
/*********************************************************************/
|
||||
|
||||
// Tree Support Maximum Branch Angle
|
||||
// The maximum angle of the branches, when the branches have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach.
|
||||
// minimum: 0, minimum warning: 20, maximum: 89, maximum warning": 85
|
||||
double support_tree_angle { 60. * M_PI / 180. };
|
||||
// Tree Support Branch Diameter Angle
|
||||
// The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length.
|
||||
// A bit of an angle can increase stability of the tree support.
|
||||
// minimum: 0, maximum: 89.9999, maximum warning: 15
|
||||
double support_tree_branch_diameter_angle { 5. * M_PI / 180. };
|
||||
// Tree Support Branch Distance
|
||||
// How far apart the branches need to be when they touch the model. Making this distance small will cause
|
||||
// the tree support to touch the model at more points, causing better overhang but making support harder to remove.
|
||||
coord_t support_tree_branch_distance { scaled<coord_t>(1.) };
|
||||
// Tree Support Branch Diameter
|
||||
// The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this.
|
||||
// minimum: 0.001, minimum warning: support_line_width * 2
|
||||
coord_t support_tree_branch_diameter { scaled<coord_t>(2.) };
|
||||
|
||||
/*********************************************************************/
|
||||
/* Parameters new to the Thomas Rahm's tree supports implementation: */
|
||||
/*********************************************************************/
|
||||
|
||||
// Tree Support Preferred Branch Angle
|
||||
// The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster.
|
||||
// minimum: 0, minimum warning: 10, maximum: support_tree_angle, maximum warning: support_tree_angle-1
|
||||
double support_tree_angle_slow { 50. * M_PI / 180. };
|
||||
// Tree Support Diameter Increase To Model
|
||||
// The most the diameter of a branch that has to connect to the model may increase by merging with branches that could reach the buildplate.
|
||||
// Increasing this reduces print time, but increases the area of support that rests on model
|
||||
// minimum: 0
|
||||
coord_t support_tree_max_diameter_increase_by_merges_when_support_to_model { scaled<coord_t>(1.0) };
|
||||
// Tree Support Minimum Height To Model
|
||||
// How tall a branch has to be if it is placed on the model. Prevents small blobs of support. This setting is ignored when a branch is supporting a support roof.
|
||||
// minimum: 0, maximum warning: 5
|
||||
coord_t support_tree_min_height_to_model { scaled<coord_t>(1.0) };
|
||||
// Tree Support Inital Layer Diameter
|
||||
// Diameter every branch tries to achieve when reaching the buildplate. Improves bed adhesion.
|
||||
// minimum: 0, maximum warning: 20
|
||||
coord_t support_tree_bp_diameter { scaled<coord_t>(7.5) };
|
||||
// Tree Support Branch Density
|
||||
// Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs,
|
||||
// but the supports are harder to remove. Use Support Roof for very high values or ensure support density is similarly high at the top.
|
||||
// 5%-35%
|
||||
double support_tree_top_rate { 15. };
|
||||
// Tree Support Tip Diameter
|
||||
// The diameter of the top of the tip of the branches of tree support.
|
||||
// minimum: min_wall_line_width, minimum warning: min_wall_line_width+0.05, maximum_value: support_tree_branch_diameter, value: support_line_width
|
||||
coord_t support_tree_tip_diameter { scaled<coord_t>(0.4) };
|
||||
|
||||
// Support Interface Priority
|
||||
// How support interface and support will interact when they overlap. Currently only implemented for support roof.
|
||||
//enum support_interface_priority { support_lines_overwrite_interface_area };
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief This struct contains settings used in the tree support. Thanks to this most functions do not need to know of meshes etc. Also makes the code shorter.
|
||||
*/
|
||||
struct TreeSupportSettings
|
||||
{
|
||||
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)
|
||||
: angle(mesh_group_settings.support_tree_angle),
|
||||
angle_slow(mesh_group_settings.support_tree_angle_slow),
|
||||
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((angle < M_PI / 2.) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits<coord_t>::max()),
|
||||
maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(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),
|
||||
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large
|
||||
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),
|
||||
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::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
double angle;
|
||||
double angle_slow;
|
||||
std::vector<coord_t> known_z;
|
||||
|
||||
public:
|
||||
// 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.
|
||||
inline static bool soluble = false;
|
||||
/*!
|
||||
* \brief Width of a single line of support.
|
||||
*/
|
||||
coord_t support_line_width;
|
||||
/*!
|
||||
* \brief Height of a single layer
|
||||
*/
|
||||
coord_t layer_height;
|
||||
/*!
|
||||
* \brief Radius of a branch when it has left the tip.
|
||||
*/
|
||||
coord_t branch_radius;
|
||||
/*!
|
||||
* \brief smallest allowed radius, required to ensure that even at DTT 0 every circle will still be printed
|
||||
*/
|
||||
coord_t min_radius;
|
||||
/*!
|
||||
* \brief How far an influence area may move outward every layer at most.
|
||||
*/
|
||||
coord_t maximum_move_distance;
|
||||
/*!
|
||||
* \brief How far every influence area will move outward every layer if possible.
|
||||
*/
|
||||
coord_t maximum_move_distance_slow;
|
||||
/*!
|
||||
* \brief Amount of bottom layers. 0 if disabled.
|
||||
*/
|
||||
size_t support_bottom_layers;
|
||||
/*!
|
||||
* \brief Amount of effectiveDTT increases are required to reach branch radius.
|
||||
*/
|
||||
size_t tip_layers;
|
||||
/*!
|
||||
* \brief Factor by which to increase the branch radius.
|
||||
*/
|
||||
double branch_radius_increase_per_layer;
|
||||
/*!
|
||||
* \brief How much a branch resting on the model may grow in radius by merging with branches that can reach the buildplate.
|
||||
*/
|
||||
coord_t max_to_model_radius_increase;
|
||||
/*!
|
||||
* \brief If smaller (in layers) than that, all branches to model will be deleted
|
||||
*/
|
||||
size_t min_dtt_to_model;
|
||||
/*!
|
||||
* \brief Increase radius in the resulting drawn branches, even if the avoidance does not allow it. Will be cut later to still fit.
|
||||
*/
|
||||
coord_t increase_radius_until_radius;
|
||||
/*!
|
||||
* \brief Same as increase_radius_until_radius, but contains the DTT at which the radius will be reached.
|
||||
*/
|
||||
size_t increase_radius_until_layer;
|
||||
/*!
|
||||
* \brief True if the branches may connect to the model.
|
||||
*/
|
||||
bool support_rests_on_model;
|
||||
/*!
|
||||
* \brief How far should support be from the model.
|
||||
*/
|
||||
coord_t xy_distance;
|
||||
/*!
|
||||
* \brief Radius a branch should have when reaching the buildplate.
|
||||
*/
|
||||
coord_t bp_radius;
|
||||
/*!
|
||||
* \brief The layer index at which an increase in radius may be required to reach the bp_radius.
|
||||
*/
|
||||
LayerIndex layer_start_bp_radius;
|
||||
/*!
|
||||
* \brief Factor by which to increase the branch radius to reach the required bp_radius at layer 0. Note that this radius increase will not happen in the tip, to ensure the tip is structurally sound.
|
||||
*/
|
||||
double bp_radius_increase_per_layer;
|
||||
/*!
|
||||
* \brief minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance-
|
||||
*/
|
||||
coord_t xy_min_distance;
|
||||
/*!
|
||||
* \brief Amount of layers distance required the top of the support to the model
|
||||
*/
|
||||
size_t z_distance_top_layers;
|
||||
/*!
|
||||
* \brief Amount of layers distance required from the top of the model to the bottom of a support structure.
|
||||
*/
|
||||
size_t z_distance_bottom_layers;
|
||||
/*!
|
||||
* \brief used for performance optimization at the support floor. Should have no impact on the resulting tree.
|
||||
*/
|
||||
size_t performance_interface_skip_layers;
|
||||
/*!
|
||||
* \brief User specified angles for the support infill.
|
||||
*/
|
||||
// std::vector<double> support_infill_angles;
|
||||
/*!
|
||||
* \brief User specified angles for the support roof infill.
|
||||
*/
|
||||
std::vector<double> support_roof_angles;
|
||||
/*!
|
||||
* \brief Pattern used in the support roof. May contain non relevant data if support roof is disabled.
|
||||
*/
|
||||
SupportMaterialInterfacePattern roof_pattern;
|
||||
/*!
|
||||
* \brief Pattern used in the support infill.
|
||||
*/
|
||||
SupportMaterialPattern support_pattern;
|
||||
/*!
|
||||
* \brief Line width of the support roof.
|
||||
*/
|
||||
coord_t support_roof_line_width;
|
||||
/*!
|
||||
* \brief Distance between support infill lines.
|
||||
*/
|
||||
coord_t support_line_spacing;
|
||||
/*!
|
||||
* \brief Offset applied to the support floor area.
|
||||
*/
|
||||
coord_t support_bottom_offset;
|
||||
/*
|
||||
* \brief Amount of walls the support area will have.
|
||||
*/
|
||||
int support_wall_count;
|
||||
/*
|
||||
* \brief Maximum allowed deviation when simplifying.
|
||||
*/
|
||||
coord_t resolution;
|
||||
/*
|
||||
* \brief Distance between the lines of the roof.
|
||||
*/
|
||||
coord_t support_roof_line_distance;
|
||||
/*
|
||||
* \brief How overlaps of an interface area with a support area should be handled.
|
||||
*/
|
||||
InterfacePreference interface_preference;
|
||||
|
||||
/*
|
||||
* \brief The infill class wants a settings object. This one will be the correct one for all settings it uses.
|
||||
*/
|
||||
TreeSupportMeshGroupSettings settings;
|
||||
|
||||
/*
|
||||
* \brief Minimum thickness of any model features.
|
||||
*/
|
||||
coord_t min_feature_size;
|
||||
|
||||
// Extra raft layers below the object.
|
||||
std::vector<coordf_t> raft_layers;
|
||||
|
||||
public:
|
||||
bool operator==(const TreeSupportSettings& other) const
|
||||
{
|
||||
return branch_radius == other.branch_radius && tip_layers == other.tip_layers && branch_radius_increase_per_layer == other.branch_radius_increase_per_layer && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius &&
|
||||
bp_radius_increase_per_layer == other.bp_radius_increase_per_layer && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance &&
|
||||
xy_distance - xy_min_distance == other.xy_distance - other.xy_min_distance && // if the delta of xy_min_distance and xy_distance is different the collision areas have to be recalculated.
|
||||
support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width &&
|
||||
support_line_spacing == other.support_line_spacing && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless.
|
||||
support_bottom_offset == other.support_bottom_offset && support_wall_count == other.support_wall_count && support_pattern == other.support_pattern && roof_pattern == other.roof_pattern && // can not be set on a per-mesh basis currently, so code to enable processing different roof patterns in the same iteration seems useless.
|
||||
support_roof_angles == other.support_roof_angles &&
|
||||
//support_infill_angles == other.support_infill_angles &&
|
||||
increase_radius_until_radius == other.increase_radius_until_radius && support_bottom_layers == other.support_bottom_layers && layer_height == other.layer_height && z_distance_top_layers == other.z_distance_top_layers && resolution == other.resolution && // Infill generation depends on deviation and resolution.
|
||||
support_roof_line_distance == other.support_roof_line_distance && interface_preference == other.interface_preference
|
||||
&& min_feature_size == other.min_feature_size // interface_preference should be identical to ensure the tree will correctly interact with the roof.
|
||||
// The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry
|
||||
#if 0
|
||||
&& (interface_preference == InterfacePreference::InterfaceAreaOverwritesSupport || interface_preference == InterfacePreference::SupportAreaOverwritesInterface
|
||||
// Perimeter generator parameters
|
||||
||
|
||||
(settings.get<bool>("fill_outline_gaps") == other.settings.get<bool>("fill_outline_gaps") &&
|
||||
settings.get<coord_t>("min_bead_width") == other.settings.get<coord_t>("min_bead_width") &&
|
||||
settings.get<double>("wall_transition_angle") == other.settings.get<double>("wall_transition_angle") &&
|
||||
settings.get<coord_t>("wall_transition_length") == other.settings.get<coord_t>("wall_transition_length") &&
|
||||
settings.get<Ratio>("wall_split_middle_threshold") == other.settings.get<Ratio>("wall_split_middle_threshold") &&
|
||||
settings.get<Ratio>("wall_add_middle_threshold") == other.settings.get<Ratio>("wall_add_middle_threshold") &&
|
||||
settings.get<int>("wall_distribution_count") == other.settings.get<int>("wall_distribution_count") &&
|
||||
settings.get<coord_t>("wall_transition_filter_distance") == other.settings.get<coord_t>("wall_transition_filter_distance") &&
|
||||
settings.get<coord_t>("wall_transition_filter_deviation") == other.settings.get<coord_t>("wall_transition_filter_deviation") &&
|
||||
settings.get<coord_t>("wall_line_width_x") == other.settings.get<coord_t>("wall_line_width_x") &&
|
||||
settings.get<int>("meshfix_maximum_extrusion_area_deviation") == other.settings.get<int>("meshfix_maximum_extrusion_area_deviation"))
|
||||
)
|
||||
#endif
|
||||
&& raft_layers == other.raft_layers
|
||||
;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the Radius part will have based on numeric values.
|
||||
* \param distance_to_top[in] The effective distance_to_top of the element
|
||||
* \param elephant_foot_increases[in] The elephant_foot_increases of the element.
|
||||
* \return The radius an element with these attributes would have.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t getRadius(size_t distance_to_top, const double elephant_foot_increases = 0) const
|
||||
{
|
||||
return (distance_to_top <= tip_layers ? min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : // tip
|
||||
branch_radius + // base
|
||||
(distance_to_top - tip_layers) * branch_radius_increase_per_layer)
|
||||
+ // gradual increase
|
||||
elephant_foot_increases * (std::max(bp_radius_increase_per_layer - branch_radius_increase_per_layer, 0.0));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the Radius an element should at least have at a given layer.
|
||||
* \param layer_idx[in] The layer.
|
||||
* \return The radius every element should aim to achieve.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t recommendedMinRadius(LayerIndex layer_idx) const
|
||||
{
|
||||
double num_layers_widened = layer_start_bp_radius - layer_idx;
|
||||
return num_layers_widened > 0 ? branch_radius + num_layers_widened * bp_radius_increase_per_layer : 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Return on which z in microns the layer will be printed. Used only for support infill line generation.
|
||||
* \param layer_idx[in] The layer.
|
||||
* \return The radius every element should aim to achieve.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t getActualZ(LayerIndex layer_idx)
|
||||
{
|
||||
return layer_idx < coord_t(known_z.size()) ? known_z[layer_idx] : (layer_idx - known_z.size()) * layer_height + known_z.size() ? known_z.back() : 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Set the z every Layer is printed at. Required for getActualZ to work
|
||||
* \param z[in] The z every LayerIndex is printed. Vector is used as a map<LayerIndex,coord_t> with the index of each element being the corresponding LayerIndex
|
||||
* \return The radius every element should aim to achieve.
|
||||
*/
|
||||
void setActualZ(std::vector<coord_t>& z)
|
||||
{
|
||||
known_z = z;
|
||||
}
|
||||
};
|
||||
|
||||
inline 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)
|
||||
{
|
||||
return layer_idx >= config.raft_layers.size() ?
|
||||
slicing_params.object_print_z_min + slicing_params.first_object_layer_height + (layer_idx - config.raft_layers.size()) * slicing_params.layer_height :
|
||||
config.raft_layers[layer_idx];
|
||||
}
|
||||
// Lowest collision layer
|
||||
inline LayerIndex layer_idx_ceil(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
|
||||
{
|
||||
return
|
||||
LayerIndex(config.raft_layers.size()) +
|
||||
std::max<LayerIndex>(0, ceil((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
|
||||
}
|
||||
// Highest collision layer
|
||||
inline LayerIndex layer_idx_floor(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
|
||||
{
|
||||
return
|
||||
LayerIndex(config.raft_layers.size()) +
|
||||
std::max<LayerIndex>(0, floor((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
|
||||
}
|
||||
|
||||
inline SupportGeneratorLayer& layer_initialize(
|
||||
SupportGeneratorLayer &layer_new,
|
||||
const SlicingParameters &slicing_params,
|
||||
const TreeSupportSettings &config,
|
||||
const size_t layer_idx)
|
||||
{
|
||||
layer_new.print_z = layer_z(slicing_params, config, layer_idx);
|
||||
layer_new.bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0;
|
||||
layer_new.height = layer_new.print_z - layer_new.bottom_z;
|
||||
return layer_new;
|
||||
}
|
||||
|
||||
// Using the std::deque as an allocator.
|
||||
inline SupportGeneratorLayer& layer_allocate_unguarded(
|
||||
SupportGeneratorLayerStorage &layer_storage,
|
||||
SupporLayerType layer_type,
|
||||
const SlicingParameters &slicing_params,
|
||||
const TreeSupportSettings &config,
|
||||
size_t layer_idx)
|
||||
{
|
||||
SupportGeneratorLayer &layer = layer_storage.allocate_unguarded(layer_type);
|
||||
return layer_initialize(layer, slicing_params, config, layer_idx);
|
||||
}
|
||||
|
||||
inline SupportGeneratorLayer& layer_allocate(
|
||||
SupportGeneratorLayerStorage &layer_storage,
|
||||
SupporLayerType layer_type,
|
||||
const SlicingParameters &slicing_params,
|
||||
const TreeSupportSettings &config,
|
||||
size_t layer_idx)
|
||||
{
|
||||
SupportGeneratorLayer &layer = layer_storage.allocate(layer_type);
|
||||
return layer_initialize(layer, slicing_params, config, layer_idx);
|
||||
}
|
||||
|
||||
// Used by generate_initial_areas() in parallel by multiple layers.
|
||||
class InterfacePlacer {
|
||||
public:
|
||||
InterfacePlacer(
|
||||
const SlicingParameters &slicing_parameters,
|
||||
const SupportParameters &support_parameters,
|
||||
const TreeSupportSettings &config,
|
||||
SupportGeneratorLayerStorage &layer_storage,
|
||||
SupportGeneratorLayersPtr &top_contacts,
|
||||
SupportGeneratorLayersPtr &top_interfaces,
|
||||
SupportGeneratorLayersPtr &top_base_interfaces)
|
||||
:
|
||||
slicing_parameters(slicing_parameters), support_parameters(support_parameters), config(config),
|
||||
layer_storage(layer_storage), top_contacts(top_contacts), top_interfaces(top_interfaces), top_base_interfaces(top_base_interfaces)
|
||||
{}
|
||||
InterfacePlacer(const InterfacePlacer& rhs) :
|
||||
slicing_parameters(rhs.slicing_parameters), support_parameters(rhs.support_parameters), config(rhs.config),
|
||||
layer_storage(rhs.layer_storage), top_contacts(rhs.top_contacts), top_interfaces(rhs.top_interfaces), top_base_interfaces(rhs.top_base_interfaces)
|
||||
{}
|
||||
|
||||
const SlicingParameters &slicing_parameters;
|
||||
const SupportParameters &support_parameters;
|
||||
const TreeSupportSettings &config;
|
||||
SupportGeneratorLayersPtr& top_contacts_mutable() { return this->top_contacts; }
|
||||
|
||||
public:
|
||||
// Insert the contact layer and some of the inteface and base interface layers below.
|
||||
void add_roofs(std::vector<Polygons> &&new_roofs, const size_t insert_layer_idx)
|
||||
{
|
||||
if (! new_roofs.empty()) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
|
||||
for (size_t idx = 0; idx < new_roofs.size(); ++ idx)
|
||||
if (! new_roofs[idx].empty())
|
||||
add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx, idx);
|
||||
}
|
||||
}
|
||||
|
||||
void add_roof(Polygons &&new_roof, const size_t insert_layer_idx, const size_t dtt_tip)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
|
||||
add_roof_unguarded(std::move(new_roof), insert_layer_idx, dtt_tip);
|
||||
}
|
||||
|
||||
// called by sample_overhang_area()
|
||||
void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
|
||||
this->add_roof_unguarded(std::move(overhang_areas), 0, std::min(dtt_roof, this->support_parameters.num_top_interface_layers));
|
||||
}
|
||||
|
||||
void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
|
||||
{
|
||||
assert(support_parameters.has_top_contacts);
|
||||
assert(dtt_roof <= support_parameters.num_top_interface_layers);
|
||||
SupportGeneratorLayersPtr &layers =
|
||||
dtt_roof == 0 ? this->top_contacts :
|
||||
dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces;
|
||||
SupportGeneratorLayer*& l = layers[insert_layer_idx];
|
||||
if (l == nullptr)
|
||||
l = &layer_allocate_unguarded(layer_storage, dtt_roof == 0 ? SupporLayerType::sltTopContact : SupporLayerType::sltTopInterface,
|
||||
slicing_parameters, config, insert_layer_idx);
|
||||
// will be unioned in finalize_interface_and_support_areas()
|
||||
append(l->polygons, std::move(new_roofs));
|
||||
}
|
||||
|
||||
private:
|
||||
// Outputs
|
||||
SupportGeneratorLayerStorage &layer_storage;
|
||||
SupportGeneratorLayersPtr &top_contacts;
|
||||
SupportGeneratorLayersPtr &top_interfaces;
|
||||
SupportGeneratorLayersPtr &top_base_interfaces;
|
||||
|
||||
// Mutexes, guards
|
||||
std::mutex m_mutex_layer_storage;
|
||||
};
|
||||
|
||||
} // namespace TreeSupport3D
|
||||
} // namespace slic3r
|
@ -1,605 +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.
|
||||
|
||||
#ifndef slic3r_TreeSupport_hpp
|
||||
#define slic3r_TreeSupport_hpp
|
||||
|
||||
#include "TreeModelVolumes.hpp"
|
||||
#include "Point.hpp"
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Utils.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
|
||||
{
|
||||
|
||||
// Forward declarations
|
||||
class TreeSupport;
|
||||
class Print;
|
||||
class PrintObject;
|
||||
class SupportGeneratorLayer;
|
||||
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
|
||||
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
|
||||
|
||||
namespace TreeSupport3D
|
||||
{
|
||||
|
||||
using LayerIndex = int;
|
||||
|
||||
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
|
||||
static constexpr const coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled<coord_t>(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR);
|
||||
static constexpr const coord_t SUPPORT_TREE_COLLISION_RESOLUTION = scaled<coord_t>(0.5);
|
||||
|
||||
// The number of vertices in each circle.
|
||||
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
|
||||
static constexpr const bool SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true;
|
||||
|
||||
enum class InterfacePreference
|
||||
{
|
||||
InterfaceAreaOverwritesSupport,
|
||||
SupportAreaOverwritesInterface,
|
||||
InterfaceLinesOverwriteSupport,
|
||||
SupportLinesOverwriteInterface,
|
||||
Nothing
|
||||
};
|
||||
|
||||
struct AreaIncreaseSettings
|
||||
{
|
||||
AreaIncreaseSettings(
|
||||
TreeModelVolumes::AvoidanceType type = TreeModelVolumes::AvoidanceType::Fast, coord_t increase_speed = 0,
|
||||
bool increase_radius = false, bool no_error = false, bool use_min_distance = false, bool move = false) :
|
||||
increase_speed{ increase_speed }, type{ type }, increase_radius{ increase_radius }, no_error{ no_error }, use_min_distance{ use_min_distance }, move{ move } {}
|
||||
|
||||
coord_t increase_speed;
|
||||
// Packing for smaller memory footprint of SupportElementState && SupportElementMerging
|
||||
TreeModelVolumes::AvoidanceType type;
|
||||
bool increase_radius : 1;
|
||||
bool no_error : 1;
|
||||
bool use_min_distance : 1;
|
||||
bool move : 1;
|
||||
bool operator==(const AreaIncreaseSettings& other) const
|
||||
{
|
||||
return type == other.type &&
|
||||
increase_speed == other.increase_speed &&
|
||||
increase_radius == other.increase_radius &&
|
||||
no_error == other.no_error &&
|
||||
use_min_distance == other.use_min_distance &&
|
||||
move == other.move;
|
||||
}
|
||||
};
|
||||
|
||||
struct TreeSupportSettings;
|
||||
|
||||
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
|
||||
struct SupportElementStateBits {
|
||||
SupportElementStateBits() :
|
||||
to_buildplate(false),
|
||||
to_model_gracious(false),
|
||||
use_min_xy_dist(false),
|
||||
supports_roof(false),
|
||||
can_use_safe_radius(false),
|
||||
skip_ovalisation(false),
|
||||
deleted(false),
|
||||
marked(false)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* \brief The element trys to reach the buildplate
|
||||
*/
|
||||
bool to_buildplate : 1;
|
||||
|
||||
/*!
|
||||
* \brief Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
|
||||
*/
|
||||
bool to_model_gracious : 1;
|
||||
|
||||
/*!
|
||||
* \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.
|
||||
*/
|
||||
bool use_min_xy_dist : 1;
|
||||
|
||||
/*!
|
||||
* \brief True if this Element or any parent provides support to a support roof.
|
||||
*/
|
||||
bool supports_roof : 1;
|
||||
|
||||
/*!
|
||||
* \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.
|
||||
*/
|
||||
bool can_use_safe_radius : 1;
|
||||
|
||||
/*!
|
||||
* \brief Skip the ovalisation to parent and children when generating the final circles.
|
||||
*/
|
||||
bool skip_ovalisation : 1;
|
||||
|
||||
// Not valid anymore, to be deleted.
|
||||
bool deleted : 1;
|
||||
|
||||
// General purpose flag marking a visited element.
|
||||
bool marked : 1;
|
||||
};
|
||||
|
||||
struct SupportElementState : public SupportElementStateBits
|
||||
{
|
||||
int type=0;
|
||||
coordf_t radius=0;
|
||||
float print_z=0;
|
||||
|
||||
/*!
|
||||
* \brief The layer this support elements wants reach
|
||||
*/
|
||||
LayerIndex target_height;
|
||||
|
||||
/*!
|
||||
* \brief The position this support elements wants to support on layer=target_height
|
||||
*/
|
||||
Point target_position;
|
||||
|
||||
/*!
|
||||
* \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 trys not to move until this dtt is reached, is set to 0 if the element had to move.
|
||||
*/
|
||||
uint32_t dont_move_until;
|
||||
|
||||
/*!
|
||||
* \brief Settings used to increase the influence area to its current state.
|
||||
*/
|
||||
AreaIncreaseSettings last_area_increase;
|
||||
|
||||
/*!
|
||||
* \brief Amount of roof layers that were not yet added, because the branch needed to move.
|
||||
*/
|
||||
uint32_t missing_roof_layers;
|
||||
|
||||
// called by increase_single_area() and increaseAreas()
|
||||
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState &src)
|
||||
{
|
||||
SupportElementState dst{ src };
|
||||
++ dst.distance_to_top;
|
||||
-- dst.layer_idx;
|
||||
// set to invalid as we are a new node on a new layer
|
||||
dst.result_on_layer_reset();
|
||||
dst.skip_ovalisation = false;
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
struct SupportElement
|
||||
{
|
||||
using ParentIndices =
|
||||
#ifdef NDEBUG
|
||||
// To reduce memory allocation in release mode.
|
||||
boost::container::small_vector<int32_t, 4>;
|
||||
#else // NDEBUG
|
||||
// To ease debugging.
|
||||
std::vector<int32_t>;
|
||||
#endif // NDEBUG
|
||||
|
||||
// SupportElement(const SupportElementState &state) : SupportElementState(state) {}
|
||||
SupportElement(const SupportElementState &state, Polygons &&influence_area) : state(state), influence_area(std::move(influence_area)) {}
|
||||
SupportElement(const SupportElementState &state, ParentIndices &&parents, Polygons &&influence_area) :
|
||||
state(state), parents(std::move(parents)), influence_area(std::move(influence_area)) {}
|
||||
|
||||
SupportElementState state;
|
||||
|
||||
/*!
|
||||
* \brief All elements in the layer above the current one that are supported by this element
|
||||
*/
|
||||
ParentIndices parents;
|
||||
|
||||
/*!
|
||||
* \brief The resulting influence area.
|
||||
* Will only be set in the results of createLayerPathing, and will be nullptr inside!
|
||||
*/
|
||||
Polygons influence_area;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief This struct contains settings used in the tree support. Thanks to this most functions do not need to know of meshes etc. Also makes the code shorter.
|
||||
*/
|
||||
struct TreeSupportSettings
|
||||
{
|
||||
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class.
|
||||
|
||||
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings)
|
||||
: angle(mesh_group_settings.support_tree_angle),
|
||||
angle_slow(mesh_group_settings.support_tree_angle_slow),
|
||||
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((angle < M_PI / 2.) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits<coord_t>::max()),
|
||||
maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(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),
|
||||
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large
|
||||
diameter_angle_scale_factor(sin(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height / branch_radius),
|
||||
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 * diameter_angle_scale_factor)),
|
||||
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),
|
||||
diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40? or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller.
|
||||
z_distance_top_layers(round_up_divide(mesh_group_settings.support_top_distance, layer_height)),
|
||||
z_distance_bottom_layers(round_up_divide(mesh_group_settings.support_bottom_distance, layer_height)),
|
||||
performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, 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)
|
||||
|
||||
|
||||
{
|
||||
layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
double angle;
|
||||
double angle_slow;
|
||||
std::vector<coord_t> known_z;
|
||||
|
||||
public:
|
||||
// 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.
|
||||
inline static bool soluble = false;
|
||||
/*!
|
||||
* \brief Width of a single line of support.
|
||||
*/
|
||||
coord_t support_line_width;
|
||||
/*!
|
||||
* \brief Height of a single layer
|
||||
*/
|
||||
coord_t layer_height;
|
||||
/*!
|
||||
* \brief Radius of a branch when it has left the tip.
|
||||
*/
|
||||
coord_t branch_radius;
|
||||
/*!
|
||||
* \brief smallest allowed radius, required to ensure that even at DTT 0 every circle will still be printed
|
||||
*/
|
||||
coord_t min_radius;
|
||||
/*!
|
||||
* \brief How far an influence area may move outward every layer at most.
|
||||
*/
|
||||
coord_t maximum_move_distance;
|
||||
/*!
|
||||
* \brief How far every influence area will move outward every layer if possible.
|
||||
*/
|
||||
coord_t maximum_move_distance_slow;
|
||||
/*!
|
||||
* \brief Amount of bottom layers. 0 if disabled.
|
||||
*/
|
||||
size_t support_bottom_layers;
|
||||
/*!
|
||||
* \brief Amount of effectiveDTT increases are required to reach branch radius.
|
||||
*/
|
||||
size_t tip_layers;
|
||||
/*!
|
||||
* \brief Factor by which to increase the branch radius.
|
||||
*/
|
||||
double diameter_angle_scale_factor;
|
||||
/*!
|
||||
* \brief How much a branch resting on the model may grow in radius by merging with branches that can reach the buildplate.
|
||||
*/
|
||||
coord_t max_to_model_radius_increase;
|
||||
/*!
|
||||
* \brief If smaller (in layers) than that, all branches to model will be deleted
|
||||
*/
|
||||
size_t min_dtt_to_model;
|
||||
/*!
|
||||
* \brief Increase radius in the resulting drawn branches, even if the avoidance does not allow it. Will be cut later to still fit.
|
||||
*/
|
||||
coord_t increase_radius_until_radius;
|
||||
/*!
|
||||
* \brief Same as increase_radius_until_radius, but contains the DTT at which the radius will be reached.
|
||||
*/
|
||||
size_t increase_radius_until_layer;
|
||||
/*!
|
||||
* \brief True if the branches may connect to the model.
|
||||
*/
|
||||
bool support_rests_on_model;
|
||||
/*!
|
||||
* \brief How far should support be from the model.
|
||||
*/
|
||||
coord_t xy_distance;
|
||||
/*!
|
||||
* \brief Radius a branch should have when reaching the buildplate.
|
||||
*/
|
||||
coord_t bp_radius;
|
||||
/*!
|
||||
* \brief The layer index at which an increase in radius may be required to reach the bp_radius.
|
||||
*/
|
||||
coord_t layer_start_bp_radius;
|
||||
/*!
|
||||
* \brief Factor by which to increase the branch radius to reach the required bp_radius at layer 0. Note that this radius increase will not happen in the tip, to ensure the tip is structurally sound.
|
||||
*/
|
||||
double diameter_scale_bp_radius;
|
||||
/*!
|
||||
* \brief minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance-
|
||||
*/
|
||||
coord_t xy_min_distance;
|
||||
/*!
|
||||
* \brief Amount of layers distance required the top of the support to the model
|
||||
*/
|
||||
size_t z_distance_top_layers;
|
||||
/*!
|
||||
* \brief Amount of layers distance required from the top of the model to the bottom of a support structure.
|
||||
*/
|
||||
size_t z_distance_bottom_layers;
|
||||
/*!
|
||||
* \brief used for performance optimization at the support floor. Should have no impact on the resulting tree.
|
||||
*/
|
||||
size_t performance_interface_skip_layers;
|
||||
/*!
|
||||
* \brief User specified angles for the support infill.
|
||||
*/
|
||||
// std::vector<double> support_infill_angles;
|
||||
/*!
|
||||
* \brief User specified angles for the support roof infill.
|
||||
*/
|
||||
std::vector<double> support_roof_angles;
|
||||
/*!
|
||||
* \brief Pattern used in the support roof. May contain non relevant data if support roof is disabled.
|
||||
*/
|
||||
SupportMaterialInterfacePattern roof_pattern;
|
||||
/*!
|
||||
* \brief Pattern used in the support infill.
|
||||
*/
|
||||
SupportMaterialPattern support_pattern;
|
||||
/*!
|
||||
* \brief Line width of the support roof.
|
||||
*/
|
||||
coord_t support_roof_line_width;
|
||||
/*!
|
||||
* \brief Distance between support infill lines.
|
||||
*/
|
||||
coord_t support_line_spacing;
|
||||
/*!
|
||||
* \brief Offset applied to the support floor area.
|
||||
*/
|
||||
coord_t support_bottom_offset;
|
||||
/*
|
||||
* \brief Amount of walls the support area will have.
|
||||
*/
|
||||
int support_wall_count;
|
||||
/*
|
||||
* \brief Maximum allowed deviation when simplifying.
|
||||
*/
|
||||
coord_t resolution;
|
||||
/*
|
||||
* \brief Distance between the lines of the roof.
|
||||
*/
|
||||
coord_t support_roof_line_distance;
|
||||
/*
|
||||
* \brief How overlaps of an interface area with a support area should be handled.
|
||||
*/
|
||||
InterfacePreference interface_preference;
|
||||
|
||||
/*
|
||||
* \brief The infill class wants a settings object. This one will be the correct one for all settings it uses.
|
||||
*/
|
||||
TreeSupportMeshGroupSettings settings;
|
||||
|
||||
/*
|
||||
* \brief Minimum thickness of any model features.
|
||||
*/
|
||||
coord_t min_feature_size;
|
||||
|
||||
public:
|
||||
bool operator==(const TreeSupportSettings& other) const
|
||||
{
|
||||
return branch_radius == other.branch_radius && tip_layers == other.tip_layers && diameter_angle_scale_factor == other.diameter_angle_scale_factor && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius && diameter_scale_bp_radius == other.diameter_scale_bp_radius && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance && // as a recalculation of the collision areas is required to set a new min_radius.
|
||||
xy_distance - xy_min_distance == other.xy_distance - other.xy_min_distance && // if the delta of xy_min_distance and xy_distance is different the collision areas have to be recalculated.
|
||||
support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width &&
|
||||
support_line_spacing == other.support_line_spacing && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless.
|
||||
support_bottom_offset == other.support_bottom_offset && support_wall_count == other.support_wall_count && support_pattern == other.support_pattern && roof_pattern == other.roof_pattern && // can not be set on a per-mesh basis currently, so code to enable processing different roof patterns in the same iteration seems useless.
|
||||
support_roof_angles == other.support_roof_angles &&
|
||||
//support_infill_angles == other.support_infill_angles &&
|
||||
increase_radius_until_radius == other.increase_radius_until_radius && support_bottom_layers == other.support_bottom_layers && layer_height == other.layer_height && z_distance_top_layers == other.z_distance_top_layers && resolution == other.resolution && // Infill generation depends on deviation and resolution.
|
||||
support_roof_line_distance == other.support_roof_line_distance && interface_preference == other.interface_preference
|
||||
&& min_feature_size == other.min_feature_size // interface_preference should be identical to ensure the tree will correctly interact with the roof.
|
||||
// The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry
|
||||
#if 0
|
||||
&& (interface_preference == InterfacePreference::InterfaceAreaOverwritesSupport || interface_preference == InterfacePreference::SupportAreaOverwritesInterface
|
||||
// Perimeter generator parameters
|
||||
||
|
||||
(settings.get<bool>("fill_outline_gaps") == other.settings.get<bool>("fill_outline_gaps") &&
|
||||
settings.get<coord_t>("min_bead_width") == other.settings.get<coord_t>("min_bead_width") &&
|
||||
settings.get<double>("wall_transition_angle") == other.settings.get<double>("wall_transition_angle") &&
|
||||
settings.get<coord_t>("wall_transition_length") == other.settings.get<coord_t>("wall_transition_length") &&
|
||||
settings.get<Ratio>("wall_split_middle_threshold") == other.settings.get<Ratio>("wall_split_middle_threshold") &&
|
||||
settings.get<Ratio>("wall_add_middle_threshold") == other.settings.get<Ratio>("wall_add_middle_threshold") &&
|
||||
settings.get<int>("wall_distribution_count") == other.settings.get<int>("wall_distribution_count") &&
|
||||
settings.get<coord_t>("wall_transition_filter_distance") == other.settings.get<coord_t>("wall_transition_filter_distance") &&
|
||||
settings.get<coord_t>("wall_transition_filter_deviation") == other.settings.get<coord_t>("wall_transition_filter_deviation") &&
|
||||
settings.get<coord_t>("wall_line_width_x") == other.settings.get<coord_t>("wall_line_width_x") &&
|
||||
settings.get<int>("meshfix_maximum_extrusion_area_deviation") == other.settings.get<int>("meshfix_maximum_extrusion_area_deviation"))
|
||||
)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \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 SupportElementState &elem) const
|
||||
{
|
||||
return elem.effective_radius_height < increase_radius_until_layer ? (elem.distance_to_top < increase_radius_until_layer ? elem.distance_to_top : increase_radius_until_layer) : elem.effective_radius_height;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the Radius part will have based on numeric values.
|
||||
* \param distance_to_top[in] The effective distance_to_top of the element
|
||||
* \param elephant_foot_increases[in] The elephant_foot_increases of the element.
|
||||
* \return The radius an element with these attributes would have.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t getRadius(size_t distance_to_top, const double elephant_foot_increases = 0) const
|
||||
{
|
||||
return (distance_to_top <= tip_layers ? min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : // tip
|
||||
branch_radius + // base
|
||||
branch_radius * (distance_to_top - tip_layers) * diameter_angle_scale_factor)
|
||||
+ // gradual increase
|
||||
branch_radius * elephant_foot_increases * (std::max(diameter_scale_bp_radius - diameter_angle_scale_factor, 0.0));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the Radius, that this element will have.
|
||||
* \param elem[in] The Element.
|
||||
* \return The radius the element has.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t getRadius(const SupportElementState &elem) const
|
||||
{ return getRadius(getEffectiveDTT(elem), elem.elephant_foot_increases); }
|
||||
[[nodiscard]] inline coord_t getRadius(const SupportElement &elem) const
|
||||
{ return this->getRadius(elem.state); }
|
||||
|
||||
/*!
|
||||
* \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.
|
||||
* \param elem[in] The Element.
|
||||
* \return The collision radius the element has.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t getCollisionRadius(const SupportElementState &elem) const
|
||||
{
|
||||
return getRadius(elem.effective_radius_height, elem.elephant_foot_increases);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the Radius an element should at least have at a given layer.
|
||||
* \param layer_idx[in] The layer.
|
||||
* \return The radius every element should aim to achieve.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t recommendedMinRadius(LayerIndex layer_idx) const
|
||||
{
|
||||
double scale = (layer_start_bp_radius - int(layer_idx)) * diameter_scale_bp_radius;
|
||||
return scale > 0 ? branch_radius + branch_radius * scale : 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Return on which z in microns the layer will be printed. Used only for support infill line generation.
|
||||
* \param layer_idx[in] The layer.
|
||||
* \return The radius every element should aim to achieve.
|
||||
*/
|
||||
[[nodiscard]] inline coord_t getActualZ(LayerIndex layer_idx)
|
||||
{
|
||||
return layer_idx < coord_t(known_z.size()) ? known_z[layer_idx] : (layer_idx - known_z.size()) * layer_height + known_z.size() ? known_z.back() : 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Set the z every Layer is printed at. Required for getActualZ to work
|
||||
* \param z[in] The z every LayerIndex is printed. Vector is used as a map<LayerIndex,coord_t> with the index of each element being the corresponding LayerIndex
|
||||
* \return The radius every element should aim to achieve.
|
||||
*/
|
||||
void setActualZ(std::vector<coord_t>& z)
|
||||
{
|
||||
known_z = z;
|
||||
}
|
||||
};
|
||||
|
||||
void tree_supports_show_error(std::string_view message, bool critical);
|
||||
|
||||
using SupportElements = std::deque<SupportElement>;
|
||||
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, SupportGeneratorLayersPtr& top_contacts, SupportGeneratorLayerStorage& layer_storage, std::function<void()> throw_on_cancel);
|
||||
|
||||
} // namespace TreeSupport3D
|
||||
|
||||
void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_support, std::function<void()> throw_on_cancel = []{});
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_TreeSupport_hpp */
|
Loading…
x
Reference in New Issue
Block a user