WIP Organic supports intefaces:
Further refactor of FDM support code - extracted interface routine to common. Implemented support for soluble interfaces & half soluble / half non-soluble interfaces.
This commit is contained in:
parent
89f0895dd6
commit
1e7a3216ca
@ -3,6 +3,7 @@
|
|||||||
#include "../Layer.hpp"
|
#include "../Layer.hpp"
|
||||||
#include "../Print.hpp"
|
#include "../Print.hpp"
|
||||||
#include "../Fill/FillBase.hpp"
|
#include "../Fill/FillBase.hpp"
|
||||||
|
#include "../MutablePolygon.hpp"
|
||||||
#include "../Geometry.hpp"
|
#include "../Geometry.hpp"
|
||||||
#include "../Point.hpp"
|
#include "../Point.hpp"
|
||||||
|
|
||||||
@ -118,6 +119,194 @@ void remove_bridges_from_contacts(
|
|||||||
#endif /* SLIC3R_DEBUG */
|
#endif /* SLIC3R_DEBUG */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers.
|
||||||
|
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,
|
||||||
|
// Input, will be trimmed with the newly created interface layers.
|
||||||
|
SupportGeneratorLayersPtr &intermediate_layers,
|
||||||
|
SupportGeneratorLayerStorage &layer_storage)
|
||||||
|
{
|
||||||
|
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> base_and_interface_layers;
|
||||||
|
|
||||||
|
if (! intermediate_layers.empty() && support_params.has_interfaces()) {
|
||||||
|
// For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers.
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
|
||||||
|
const bool snug_supports = config.support_material_style.value != smsGrid;
|
||||||
|
SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first;
|
||||||
|
SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second;
|
||||||
|
interface_layers.assign(intermediate_layers.size(), nullptr);
|
||||||
|
if (support_params.has_base_interfaces())
|
||||||
|
base_interface_layers.assign(intermediate_layers.size(), nullptr);
|
||||||
|
const auto smoothing_distance = support_params.support_material_interface_flow.scaled_spacing() * 1.5;
|
||||||
|
const auto minimum_island_radius = support_params.support_material_interface_flow.scaled_spacing() / support_params.interface_density;
|
||||||
|
const auto closing_distance = smoothing_distance; // scaled<float>(config.support_material_closing_radius.value);
|
||||||
|
// Insert a new layer into base_interface_layers, if intersection with base exists.
|
||||||
|
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).
|
||||||
|
bottom = intersection(
|
||||||
|
snug_supports ?
|
||||||
|
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);
|
||||||
|
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 = 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;
|
||||||
|
layer_new.height = intermediate_layer.height;
|
||||||
|
layer_new.bridging = intermediate_layer.bridging;
|
||||||
|
// Subtract the interface from the base regions.
|
||||||
|
intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons);
|
||||||
|
if (subtract)
|
||||||
|
// Trim the base interface layer with the interface layer.
|
||||||
|
layer_new.polygons = diff(std::move(layer_new.polygons), *subtract);
|
||||||
|
//FIXME filter layer_new.polygons islands by a minimum area?
|
||||||
|
// $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ];
|
||||||
|
return &layer_new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
|
||||||
|
[&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer, &support_params,
|
||||||
|
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
|
||||||
|
// this intermediate layer.
|
||||||
|
// Index of the first top contact layer intersecting the current intermediate layer.
|
||||||
|
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];
|
||||||
|
Polygons polygons_top_contact_projected_interface;
|
||||||
|
Polygons polygons_top_contact_projected_base;
|
||||||
|
Polygons polygons_bottom_contact_projected_interface;
|
||||||
|
Polygons polygons_bottom_contact_projected_base;
|
||||||
|
if (support_params.num_top_interface_layers > 0) {
|
||||||
|
// Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
|
||||||
|
coordf_t top_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + int(support_params.num_top_interface_layers) - 1)]->print_z;
|
||||||
|
coordf_t top_inteface_z = std::numeric_limits<coordf_t>::max();
|
||||||
|
if (support_params.num_top_base_interface_layers > 0)
|
||||||
|
// Some top base interface layers will be generated.
|
||||||
|
top_inteface_z = support_params.num_top_interface_layers_only() == 0 ?
|
||||||
|
// Only base interface layers to generate.
|
||||||
|
- std::numeric_limits<coordf_t>::max() :
|
||||||
|
intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + int(support_params.num_top_interface_layers_only()) - 1)]->print_z;
|
||||||
|
// Move idx_top_contact_first up until above the current print_z.
|
||||||
|
idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const SupportGeneratorLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON
|
||||||
|
// Collect the top contact areas above this intermediate layer, below top_z.
|
||||||
|
for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) {
|
||||||
|
const SupportGeneratorLayer &top_contact_layer = *top_contacts[idx_top_contact];
|
||||||
|
//FIXME maybe this adds one interface layer in excess?
|
||||||
|
if (top_contact_layer.bottom_z - EPSILON > top_z)
|
||||||
|
break;
|
||||||
|
polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface,
|
||||||
|
// For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers.
|
||||||
|
// For grid supports, merging of support regions will be performed by the projection into grid.
|
||||||
|
snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (support_params.num_bottom_interface_layers > 0) {
|
||||||
|
// Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
|
||||||
|
coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers) + 1)]->bottom_z;
|
||||||
|
coordf_t bottom_interface_z = - std::numeric_limits<coordf_t>::max();
|
||||||
|
if (support_params.num_bottom_base_interface_layers > 0)
|
||||||
|
// Some bottom base interface layers will be generated.
|
||||||
|
bottom_interface_z = support_params.num_bottom_interface_layers_only() == 0 ?
|
||||||
|
// Only base interface layers to generate.
|
||||||
|
std::numeric_limits<coordf_t>::max() :
|
||||||
|
intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers_only()))]->bottom_z;
|
||||||
|
// Move idx_bottom_contact_first up until touching bottom_z.
|
||||||
|
idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const SupportGeneratorLayer *layer){ return layer->print_z >= bottom_z - EPSILON; });
|
||||||
|
// Collect the top contact areas above this intermediate layer, below top_z.
|
||||||
|
for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) {
|
||||||
|
const SupportGeneratorLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact];
|
||||||
|
if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z)
|
||||||
|
break;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
SupportGeneratorLayer *l = layers[idx];
|
||||||
|
// Remove the layer from the source container, as it will be consumed here: It will be merged
|
||||||
|
// with the newly produced interfaces.
|
||||||
|
layers[idx] = nullptr;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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), top_interface_layer,
|
||||||
|
nullptr, polygons_top_contact_projected_interface.empty() ? SupporLayerType::BottomInterface : SupporLayerType::TopInterface);
|
||||||
|
interface_layers[idx_intermediate_layer] = interface_layer;
|
||||||
|
}
|
||||||
|
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), top_base_interface_layer,
|
||||||
|
interface_layer ? &interface_layer->polygons : nullptr, SupporLayerType::Base);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Compress contact_out, remove the nullptr items.
|
||||||
|
// 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_nulls = [](SupportGeneratorLayersPtr &in1, SupportGeneratorLayersPtr &in2) {
|
||||||
|
size_t nonzeros = std::count_if(in1.begin(), in1.end(), [](auto *l) { return l != nullptr; }) +
|
||||||
|
std::count_if(in2.begin(), in2.end(), [](auto *l) { return l != nullptr; });
|
||||||
|
remove_nulls(in1);
|
||||||
|
remove_nulls(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 std::move(out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
interface_layers = merge_remove_nulls(interface_layers, top_interface_layers);
|
||||||
|
base_interface_layers = merge_remove_nulls(base_interface_layers, top_base_interface_layers);
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
|
||||||
|
}
|
||||||
|
|
||||||
|
return base_and_interface_layers;
|
||||||
|
}
|
||||||
|
|
||||||
SupportGeneratorLayersPtr generate_raft_base(
|
SupportGeneratorLayersPtr generate_raft_base(
|
||||||
const PrintObject &object,
|
const PrintObject &object,
|
||||||
const SupportParameters &support_params,
|
const SupportParameters &support_params,
|
||||||
|
@ -21,6 +21,23 @@ void remove_bridges_from_contacts(
|
|||||||
float fw,
|
float fw,
|
||||||
Polygons &contact_polygons);
|
Polygons &contact_polygons);
|
||||||
|
|
||||||
|
// Turn some of the base layers into base interface layers.
|
||||||
|
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
|
||||||
|
// extruder to improve adhesion of the soluble filament to the base.
|
||||||
|
// For Organic supports, merge top_interface_layers & top_base_interface_layers with the interfaces
|
||||||
|
// produced by this function.
|
||||||
|
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
|
||||||
|
const PrintObjectConfig &config,
|
||||||
|
const SupportParameters &support_params,
|
||||||
|
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||||
|
const SupportGeneratorLayersPtr &top_contacts,
|
||||||
|
// Input / output, will be merged with output
|
||||||
|
SupportGeneratorLayersPtr &top_interface_layers,
|
||||||
|
SupportGeneratorLayersPtr &top_base_interface_layers,
|
||||||
|
// Input, will be trimmed with the newly created interface layers.
|
||||||
|
SupportGeneratorLayersPtr &intermediate_layers,
|
||||||
|
SupportGeneratorLayerStorage &layer_storage);
|
||||||
|
|
||||||
// Generate raft layers, also expand the 1st support layer
|
// Generate raft layers, also expand the 1st support layer
|
||||||
// in case there is no raft layer to improve support adhesion.
|
// in case there is no raft layer to improve support adhesion.
|
||||||
SupportGeneratorLayersPtr generate_raft_base(
|
SupportGeneratorLayersPtr generate_raft_base(
|
||||||
|
@ -335,14 +335,16 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
|
|||||||
|
|
||||||
// Propagate top / bottom contact layers to generate interface layers
|
// Propagate top / bottom contact layers to generate interface layers
|
||||||
// and base interface layers (for soluble interface / non souble base only)
|
// 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] = FFFSupport::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";
|
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating raft";
|
||||||
|
|
||||||
// If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled.
|
// If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled.
|
||||||
// There is also a 1st intermediate layer containing bases of support columns.
|
// There is also a 1st intermediate layer containing bases of support columns.
|
||||||
// Inflate the bases of the support columns and create the raft base under the object.
|
// Inflate the bases of the support columns and create the raft base under the object.
|
||||||
SupportGeneratorLayersPtr raft_layers = generate_raft_base(object, m_support_params, m_slicing_params, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
|
SupportGeneratorLayersPtr raft_layers = FFFSupport::generate_raft_base(object, m_support_params, m_slicing_params, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_DEBUG
|
||||||
for (const SupportGeneratorLayer *l : interface_layers)
|
for (const SupportGeneratorLayer *l : interface_layers)
|
||||||
@ -2514,169 +2516,6 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
|||||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end";
|
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(
|
|
||||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
|
||||||
const SupportGeneratorLayersPtr &top_contacts,
|
|
||||||
SupportGeneratorLayersPtr &intermediate_layers,
|
|
||||||
SupportGeneratorLayerStorage &layer_storage) const
|
|
||||||
{
|
|
||||||
// my $area_threshold = $self->interface_flow->scaled_spacing ** 2;
|
|
||||||
|
|
||||||
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_material_interface_layers > 1.
|
|
||||||
// Contact layer needs a base_interface layer, therefore run the following block if support_material_interface_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_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) &&
|
|
||||||
// Base extruder: Either "print with active extruder" not soluble.
|
|
||||||
(m_object_config->support_material_extruder.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_material_extruder.value - 1));
|
|
||||||
bool snug_supports = m_object_config->support_material_style.value != smsGrid;
|
|
||||||
int num_interface_layers_top = m_object_config->support_material_interface_layers;
|
|
||||||
int num_interface_layers_bottom = m_object_config->support_material_bottom_interface_layers;
|
|
||||||
if (num_interface_layers_bottom < 0)
|
|
||||||
num_interface_layers_bottom = num_interface_layers_top;
|
|
||||||
int num_base_interface_layers_top = soluble_interface_non_soluble_base ? std::min(num_interface_layers_top / 2, 2) : 0;
|
|
||||||
int num_base_interface_layers_bottom = soluble_interface_non_soluble_base ? std::min(num_interface_layers_bottom / 2, 2) : 0;
|
|
||||||
|
|
||||||
if (! intermediate_layers.empty() && (num_interface_layers_top > 1 || num_interface_layers_bottom > 1)) {
|
|
||||||
// For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers.
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
|
|
||||||
// Since the intermediate layer index starts at zero the number of interface layer needs to be reduced by 1.
|
|
||||||
-- num_interface_layers_top;
|
|
||||||
-- num_interface_layers_bottom;
|
|
||||||
int num_interface_layers_only_top = num_interface_layers_top - num_base_interface_layers_top;
|
|
||||||
int num_interface_layers_only_bottom = num_interface_layers_bottom - num_base_interface_layers_bottom;
|
|
||||||
interface_layers.assign(intermediate_layers.size(), nullptr);
|
|
||||||
if (num_base_interface_layers_top || num_base_interface_layers_bottom)
|
|
||||||
base_interface_layers.assign(intermediate_layers.size(), nullptr);
|
|
||||||
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_material_closing_radius.value);
|
|
||||||
// Insert a new layer into base_interface_layers, if intersection with base exists.
|
|
||||||
auto insert_layer = [&layer_storage, 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());
|
|
||||||
// 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).
|
|
||||||
bottom = intersection(
|
|
||||||
snug_supports ?
|
|
||||||
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 (! 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_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;
|
|
||||||
layer_new.height = intermediate_layer.height;
|
|
||||||
layer_new.bridging = intermediate_layer.bridging;
|
|
||||||
// Subtract the interface from the base regions.
|
|
||||||
intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons);
|
|
||||||
if (subtract)
|
|
||||||
// Trim the base interface layer with the interface layer.
|
|
||||||
layer_new.polygons = diff(std::move(layer_new.polygons), *subtract);
|
|
||||||
//FIXME filter layer_new.polygons islands by a minimum area?
|
|
||||||
// $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ];
|
|
||||||
return &layer_new;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
|
|
||||||
[&bottom_contacts, &top_contacts, &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
|
|
||||||
// this intermediate layer.
|
|
||||||
// Index of the first top contact layer intersecting the current intermediate layer.
|
|
||||||
auto idx_top_contact_first = -1;
|
|
||||||
// Index of the first bottom contact layer intersecting the current intermediate layer.
|
|
||||||
auto idx_bottom_contact_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];
|
|
||||||
Polygons polygons_top_contact_projected_interface;
|
|
||||||
Polygons polygons_top_contact_projected_base;
|
|
||||||
Polygons polygons_bottom_contact_projected_interface;
|
|
||||||
Polygons polygons_bottom_contact_projected_base;
|
|
||||||
if (num_interface_layers_top > 0) {
|
|
||||||
// Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
|
|
||||||
coordf_t top_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_top - 1)]->print_z;
|
|
||||||
coordf_t top_inteface_z = std::numeric_limits<coordf_t>::max();
|
|
||||||
if (num_base_interface_layers_top > 0)
|
|
||||||
// Some top base interface layers will be generated.
|
|
||||||
top_inteface_z = num_interface_layers_only_top == 0 ?
|
|
||||||
// Only base interface layers to generate.
|
|
||||||
- std::numeric_limits<coordf_t>::max() :
|
|
||||||
intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_only_top - 1)]->print_z;
|
|
||||||
// Move idx_top_contact_first up until above the current print_z.
|
|
||||||
idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const SupportGeneratorLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON
|
|
||||||
// Collect the top contact areas above this intermediate layer, below top_z.
|
|
||||||
for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) {
|
|
||||||
const SupportGeneratorLayer &top_contact_layer = *top_contacts[idx_top_contact];
|
|
||||||
//FIXME maybe this adds one interface layer in excess?
|
|
||||||
if (top_contact_layer.bottom_z - EPSILON > top_z)
|
|
||||||
break;
|
|
||||||
polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface,
|
|
||||||
// For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers.
|
|
||||||
// For grid supports, merging of support regions will be performed by the projection into grid.
|
|
||||||
snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (num_interface_layers_bottom > 0) {
|
|
||||||
// Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
|
|
||||||
coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_bottom + 1)]->bottom_z;
|
|
||||||
coordf_t bottom_interface_z = - std::numeric_limits<coordf_t>::max();
|
|
||||||
if (num_base_interface_layers_bottom > 0)
|
|
||||||
// Some bottom base interface layers will be generated.
|
|
||||||
bottom_interface_z = num_interface_layers_only_bottom == 0 ?
|
|
||||||
// Only base interface layers to generate.
|
|
||||||
std::numeric_limits<coordf_t>::max() :
|
|
||||||
intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only_bottom)]->bottom_z;
|
|
||||||
// Move idx_bottom_contact_first up until touching bottom_z.
|
|
||||||
idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const SupportGeneratorLayer *layer){ return layer->print_z >= bottom_z - EPSILON; });
|
|
||||||
// Collect the top contact areas above this intermediate layer, below top_z.
|
|
||||||
for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) {
|
|
||||||
const SupportGeneratorLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact];
|
|
||||||
if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z)
|
|
||||||
break;
|
|
||||||
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()) {
|
|
||||||
interface_layer = insert_layer(
|
|
||||||
intermediate_layer, polygons_bottom_contact_projected_interface, std::move(polygons_top_contact_projected_interface), nullptr,
|
|
||||||
polygons_top_contact_projected_interface.empty() ? SupporLayerType::BottomInterface : SupporLayerType::TopInterface);
|
|
||||||
interface_layers[idx_intermediate_layer] = interface_layer;
|
|
||||||
}
|
|
||||||
if (! polygons_bottom_contact_projected_base.empty() || ! polygons_top_contact_projected_base.empty())
|
|
||||||
base_interface_layers[idx_intermediate_layer] = insert_layer(
|
|
||||||
intermediate_layer, polygons_bottom_contact_projected_base, std::move(polygons_top_contact_projected_base),
|
|
||||||
interface_layer ? &interface_layer->polygons : nullptr, SupporLayerType::Base);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Compress contact_out, remove the nullptr items.
|
|
||||||
remove_nulls(interface_layers);
|
|
||||||
remove_nulls(base_interface_layers);
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
|
|
||||||
}
|
|
||||||
|
|
||||||
return base_and_interface_layers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void PrintObjectSupportMaterial::clip_by_pillars(
|
void PrintObjectSupportMaterial::clip_by_pillars(
|
||||||
const PrintObject &object,
|
const PrintObject &object,
|
||||||
|
@ -72,16 +72,6 @@ private:
|
|||||||
SupportGeneratorLayersPtr &intermediate_layers,
|
SupportGeneratorLayersPtr &intermediate_layers,
|
||||||
const std::vector<Polygons> &layer_support_areas) const;
|
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
|
// Trim support layers by an object to leave a defined gap between
|
||||||
// the support volume and the object.
|
// the support volume and the object.
|
||||||
void trim_support_layers_by_object(
|
void trim_support_layers_by_object(
|
||||||
|
@ -11,6 +11,33 @@ SupportParameters::SupportParameters(const PrintObject &object)
|
|||||||
const PrintObjectConfig &object_config = object.config();
|
const PrintObjectConfig &object_config = object.config();
|
||||||
const SlicingParameters &slicing_params = object.slicing_parameters();
|
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_material_interface_extruder.value > 0 && print_config.filament_soluble.get_at(object_config.support_material_interface_extruder.value - 1) &&
|
||||||
|
// Base extruder: Either "print with active extruder" not soluble.
|
||||||
|
(object_config.support_material_extruder.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_material_extruder.value - 1));
|
||||||
|
|
||||||
|
{
|
||||||
|
int num_top_interface_layers = std::max(0, object_config.support_material_interface_layers.value);
|
||||||
|
int num_bottom_interface_layers = object_config.support_material_bottom_interface_layers < 0 ?
|
||||||
|
num_top_interface_layers : object_config.support_material_bottom_interface_layers;
|
||||||
|
this->has_top_contacts = num_top_interface_layers > 0;
|
||||||
|
this->has_bottom_contacts = num_bottom_interface_layers > 0;
|
||||||
|
this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0;
|
||||||
|
this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0;
|
||||||
|
if (this->soluble_interface_non_soluble_base) {
|
||||||
|
// Try to support soluble dense interfaces with non-soluble dense interfaces.
|
||||||
|
this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2));
|
||||||
|
this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2));
|
||||||
|
} else {
|
||||||
|
this->num_top_base_interface_layers = 0;
|
||||||
|
this->num_bottom_base_interface_layers = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
|
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
|
||||||
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
|
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
|
||||||
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
|
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
|
||||||
@ -54,7 +81,6 @@ SupportParameters::SupportParameters(const PrintObject &object)
|
|||||||
this->can_merge_support_regions = true;
|
this->can_merge_support_regions = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double interface_spacing = object_config.support_material_interface_spacing.value + this->support_material_interface_flow.spacing();
|
double interface_spacing = object_config.support_material_interface_spacing.value + this->support_material_interface_flow.spacing();
|
||||||
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
|
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
|
||||||
double raft_interface_spacing = object_config.support_material_interface_spacing.value + this->raft_interface_flow.spacing();
|
double raft_interface_spacing = object_config.support_material_interface_spacing.value + this->raft_interface_flow.spacing();
|
||||||
|
@ -14,6 +14,30 @@ namespace FFFSupport {
|
|||||||
struct SupportParameters {
|
struct SupportParameters {
|
||||||
SupportParameters(const PrintObject &object);
|
SupportParameters(const PrintObject &object);
|
||||||
|
|
||||||
|
// 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 at the 1st print layer.
|
// Flow at the 1st print layer.
|
||||||
Flow first_layer_flow;
|
Flow first_layer_flow;
|
||||||
// Flow at the support base (neither top, nor bottom interface).
|
// Flow at the support base (neither top, nor bottom interface).
|
||||||
|
@ -87,7 +87,6 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes
|
|||||||
bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
|
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_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)))),
|
z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(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_infill_angles(mesh_group_settings.support_infill_angles),
|
||||||
support_roof_angles(mesh_group_settings.support_roof_angles),
|
support_roof_angles(mesh_group_settings.support_roof_angles),
|
||||||
roof_pattern(mesh_group_settings.support_roof_pattern),
|
roof_pattern(mesh_group_settings.support_roof_pattern),
|
||||||
@ -1077,71 +1076,53 @@ void finalize_raft_contact(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used by generate_initial_areas() in parallel by multiple layers.
|
||||||
class InterfacePlacer {
|
class InterfacePlacer {
|
||||||
public:
|
public:
|
||||||
InterfacePlacer(const SlicingParameters &slicing_parameters, const TreeModelVolumes &volumes, const TreeSupportSettings &config, bool force_tip_to_roof, size_t num_support_layers,
|
InterfacePlacer(
|
||||||
std::vector<SupportElements> &move_bounds, SupportGeneratorLayerStorage &layer_storage, SupportGeneratorLayersPtr &top_contacts) :
|
const SlicingParameters &slicing_parameters,
|
||||||
slicing_parameters(slicing_parameters), volumes(volumes), config(config), force_tip_to_roof(force_tip_to_roof),
|
const SupportParameters &support_parameters,
|
||||||
move_bounds(move_bounds), layer_storage(layer_storage), top_contacts(top_contacts) {
|
const TreeModelVolumes &volumes, const TreeSupportSettings &config,
|
||||||
|
bool force_tip_to_roof, size_t num_support_layers,
|
||||||
|
std::vector<SupportElements> &move_bounds,
|
||||||
|
SupportGeneratorLayerStorage &layer_storage,
|
||||||
|
SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &top_interfaces, SupportGeneratorLayersPtr &top_base_interfaces)
|
||||||
|
:
|
||||||
|
slicing_parameters(slicing_parameters), support_parameters(support_parameters), volumes(volumes), config(config), force_tip_to_roof(force_tip_to_roof),
|
||||||
|
move_bounds(move_bounds),
|
||||||
|
layer_storage(layer_storage), top_contacts(top_contacts), top_interfaces(top_interfaces), top_base_interfaces(top_base_interfaces) {
|
||||||
m_already_inserted.assign(num_support_layers, {});
|
m_already_inserted.assign(num_support_layers, {});
|
||||||
this->min_xy_dist = config.xy_distance > config.xy_min_distance;
|
this->min_xy_dist = config.xy_distance > config.xy_min_distance;
|
||||||
}
|
}
|
||||||
const SlicingParameters &slicing_parameters;
|
const SlicingParameters &slicing_parameters;
|
||||||
|
const SupportParameters &support_parameters;
|
||||||
const TreeModelVolumes &volumes;
|
const TreeModelVolumes &volumes;
|
||||||
const TreeSupportSettings &config;
|
const TreeSupportSettings &config;
|
||||||
bool force_tip_to_roof;
|
// Radius of the tree tip is large enough to be covered by an interface.
|
||||||
|
const bool force_tip_to_roof;
|
||||||
bool min_xy_dist;
|
bool min_xy_dist;
|
||||||
|
|
||||||
// Outputs
|
|
||||||
std::vector<SupportElements> &move_bounds;
|
|
||||||
SupportGeneratorLayerStorage &layer_storage;
|
|
||||||
SupportGeneratorLayersPtr &top_contacts;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Temps
|
|
||||||
static constexpr const auto m_base_radius = scaled<int>(0.01);
|
|
||||||
const Polygon m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) };
|
|
||||||
|
|
||||||
// Mutexes, guards
|
|
||||||
std::mutex m_mutex_movebounds;
|
|
||||||
std::mutex m_mutex_layer_storage;
|
|
||||||
std::vector<std::unordered_set<Point, PointHash>> m_already_inserted;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx)
|
// called by sample_overhang_area()
|
||||||
{
|
// Insert the contact layer and some of the inteface and base interface layers below.
|
||||||
SupportGeneratorLayer*& l = top_contacts[insert_layer_idx];
|
void add_roofs(std::vector<Polygons> &&new_roofs, const size_t insert_layer_idx)
|
||||||
if (l == nullptr)
|
|
||||||
l = &layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, insert_layer_idx);
|
|
||||||
// will be unioned in finalize_interface_and_support_areas()
|
|
||||||
append(l->polygons, std::move(new_roofs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_roof(Polygons &&new_roofs, const size_t insert_layer_idx)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
|
|
||||||
add_roof_unguarded(std::move(new_roofs), insert_layer_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_roofs(std::vector<Polygons> &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
|
|
||||||
{
|
{
|
||||||
if (! new_roofs.empty()) {
|
if (! new_roofs.empty()) {
|
||||||
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
|
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
|
||||||
for (size_t idx = 0; idx < dtt_roof; ++ idx)
|
for (size_t idx = 0; idx < new_roofs.size(); ++ idx)
|
||||||
if (! new_roofs[idx].empty())
|
if (! new_roofs[idx].empty())
|
||||||
add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx);
|
add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx, idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_roof_build_plate(Polygons &&overhang_areas)
|
// 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);
|
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
|
||||||
SupportGeneratorLayer*& l = top_contacts[0];
|
this->add_roof_unguarded(std::move(overhang_areas), 0, dtt_roof);
|
||||||
if (l == nullptr)
|
|
||||||
l = &layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, slicing_parameters, config, 0);
|
|
||||||
append(l->polygons, std::move(overhang_areas));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// called by sample_overhang_area()
|
||||||
void add_points_along_lines(
|
void add_points_along_lines(
|
||||||
// Insert points (tree tips or top contact interfaces) along these lines.
|
// Insert points (tree tips or top contact interfaces) along these lines.
|
||||||
LineInformations lines,
|
LineInformations lines,
|
||||||
@ -1150,7 +1131,7 @@ public:
|
|||||||
// Insert this number of interface layers.
|
// Insert this number of interface layers.
|
||||||
size_t roof_tip_layers,
|
size_t roof_tip_layers,
|
||||||
// True if an interface is already generated above these lines.
|
// True if an interface is already generated above these lines.
|
||||||
bool supports_roof,
|
size_t supports_roof_layers,
|
||||||
// The element tries to not move until this dtt is reached.
|
// The element tries to not move until this dtt is reached.
|
||||||
size_t dont_move_until)
|
size_t dont_move_until)
|
||||||
{
|
{
|
||||||
@ -1203,7 +1184,7 @@ public:
|
|||||||
roof_circle.translate(p.first);
|
roof_circle.translate(p.first);
|
||||||
new_roofs.emplace_back(std::move(roof_circle));
|
new_roofs.emplace_back(std::move(roof_circle));
|
||||||
}
|
}
|
||||||
this->add_roof(std::move(new_roofs), this_layer_idx);
|
this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const LineInformation &line : lines) {
|
for (const LineInformation &line : lines) {
|
||||||
@ -1215,11 +1196,33 @@ public:
|
|||||||
// don't move until
|
// don't move until
|
||||||
dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0,
|
dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0,
|
||||||
// supports roof
|
// supports roof
|
||||||
dtt_roof_tip > 0 || supports_roof,
|
dtt_roof_tip + supports_roof_layers > 0,
|
||||||
disable_ovalistation);
|
disable_ovalistation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
|
||||||
|
{
|
||||||
|
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::TopContact : SupporLayerType::TopInterface,
|
||||||
|
slicing_parameters, config, insert_layer_idx);
|
||||||
|
// will be unioned in finalize_interface_and_support_areas()
|
||||||
|
append(l->polygons, std::move(new_roofs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by this->add_points_along_lines()
|
||||||
|
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 this->add_points_along_lines()
|
||||||
void add_point_as_influence_area(std::pair<Point, LineStatus> p, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation)
|
void add_point_as_influence_area(std::pair<Point, LineStatus> p, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation)
|
||||||
{
|
{
|
||||||
bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE;
|
bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE;
|
||||||
@ -1233,8 +1236,8 @@ public:
|
|||||||
Polygons circle{ m_base_circle };
|
Polygons circle{ m_base_circle };
|
||||||
circle.front().translate(p.first);
|
circle.front().translate(p.first);
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> critical_section_movebounds(m_mutex_movebounds);
|
|
||||||
Point hash_pos = p.first / ((config.min_radius + 1) / 10);
|
Point hash_pos = p.first / ((config.min_radius + 1) / 10);
|
||||||
|
std::lock_guard<std::mutex> critical_section_movebounds(m_mutex_movebounds);
|
||||||
if (!m_already_inserted[insert_layer].count(hash_pos)) {
|
if (!m_already_inserted[insert_layer].count(hash_pos)) {
|
||||||
// normalize the point a bit to also catch points which are so close that inserting it would achieve nothing
|
// normalize the point a bit to also catch points which are so close that inserting it would achieve nothing
|
||||||
m_already_inserted[insert_layer].emplace(hash_pos);
|
m_already_inserted[insert_layer].emplace(hash_pos);
|
||||||
@ -1261,9 +1264,26 @@ public:
|
|||||||
move_bounds[insert_layer].emplace_back(state, std::move(circle));
|
move_bounds[insert_layer].emplace_back(state, std::move(circle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Outputs
|
||||||
|
std::vector<SupportElements> &move_bounds;
|
||||||
|
SupportGeneratorLayerStorage &layer_storage;
|
||||||
|
SupportGeneratorLayersPtr &top_contacts;
|
||||||
|
SupportGeneratorLayersPtr &top_interfaces;
|
||||||
|
SupportGeneratorLayersPtr &top_base_interfaces;
|
||||||
|
|
||||||
|
// Temps
|
||||||
|
static constexpr const auto m_base_radius = scaled<int>(0.01);
|
||||||
|
const Polygon m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) };
|
||||||
|
|
||||||
|
// Mutexes, guards
|
||||||
|
std::mutex m_mutex_movebounds;
|
||||||
|
std::mutex m_mutex_layer_storage;
|
||||||
|
std::vector<std::unordered_set<Point, PointHash>> m_already_inserted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Called by generate_initial_areas(), used in parallel by multiple layers.
|
||||||
// Produce
|
// Produce
|
||||||
// 1) Maximum num_support_roof_layers roof (top interface & contact) layers.
|
// 1) Maximum num_support_roof_layers roof (top interface & contact) layers.
|
||||||
// 2) Tree tips supporting either the roof layers or the object itself.
|
// 2) Tree tips supporting either the roof layers or the object itself.
|
||||||
@ -1280,13 +1300,12 @@ void sample_overhang_area(
|
|||||||
const bool large_horizontal_roof,
|
const bool large_horizontal_roof,
|
||||||
// Index of the top suport layer generated by this function.
|
// Index of the top suport layer generated by this function.
|
||||||
const size_t layer_idx,
|
const size_t layer_idx,
|
||||||
// Number of roof (contact, interface) layers between the overhang and tree tips.
|
// Maximum number of roof (contact, interface) layers between the overhang and tree tips to be generated.
|
||||||
const size_t num_support_roof_layers,
|
const size_t num_support_roof_layers,
|
||||||
//
|
//
|
||||||
const coord_t connect_length,
|
const coord_t connect_length,
|
||||||
// Configuration classes
|
// Configuration classes
|
||||||
const TreeSupportMeshGroupSettings &mesh_group_settings,
|
const TreeSupportMeshGroupSettings &mesh_group_settings,
|
||||||
const SupportParameters &support_params,
|
|
||||||
// Configuration & Output
|
// Configuration & Output
|
||||||
InterfacePlacer &interface_placer)
|
InterfacePlacer &interface_placer)
|
||||||
{
|
{
|
||||||
@ -1297,11 +1316,12 @@ void sample_overhang_area(
|
|||||||
// as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from
|
// as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from
|
||||||
// a decreasing roof, as there is no guarantee that a line will be above these points. Implementing a separate roof support behavior
|
// a decreasing roof, as there is no guarantee that a line will be above these points. Implementing a separate roof support behavior
|
||||||
// for each pattern harms maintainability as it very well could be >100 LOC
|
// for each pattern harms maintainability as it very well could be >100 LOC
|
||||||
auto generate_roof_lines = [&support_params, &mesh_group_settings](const Polygons &area, LayerIndex layer_idx) -> Polylines {
|
auto generate_roof_lines = [&interface_placer, &mesh_group_settings](const Polygons &area, LayerIndex layer_idx) -> Polylines {
|
||||||
return generate_support_infill_lines(area, support_params, true, layer_idx, mesh_group_settings.support_roof_line_distance);
|
return generate_support_infill_lines(area, interface_placer.support_parameters, true, layer_idx, mesh_group_settings.support_roof_line_distance);
|
||||||
};
|
};
|
||||||
|
|
||||||
LineInformations overhang_lines;
|
LineInformations overhang_lines;
|
||||||
|
// Track how many top contact / interface layers were already generated.
|
||||||
size_t dtt_roof = 0;
|
size_t dtt_roof = 0;
|
||||||
size_t layer_generation_dtt = 0;
|
size_t layer_generation_dtt = 0;
|
||||||
|
|
||||||
@ -1325,9 +1345,9 @@ void sample_overhang_area(
|
|||||||
}
|
}
|
||||||
Polygons overhang_area_next = diff(overhang_area, forbidden_next);
|
Polygons overhang_area_next = diff(overhang_area, forbidden_next);
|
||||||
if (area(overhang_area_next) < mesh_group_settings.minimum_roof_area) {
|
if (area(overhang_area_next) < mesh_group_settings.minimum_roof_area) {
|
||||||
// next layer down the roof area would be to small so we have to insert our roof support here. Also convert squaremicrons to squaremilimeter
|
// Next layer down the roof area would be to small so we have to insert our roof support here.
|
||||||
if (dtt_roof != 0) {
|
if (dtt_roof > 0) {
|
||||||
size_t dtt_before = dtt_roof > 0 ? dtt_roof - 1 : 0;
|
size_t dtt_before = dtt_roof - 1;
|
||||||
// Produce support head points supporting an interface layer: First produce the interface lines, then sample them.
|
// Produce support head points supporting an interface layer: First produce the interface lines, then sample them.
|
||||||
overhang_lines = split_lines(
|
overhang_lines = split_lines(
|
||||||
convert_lines_to_internal(interface_placer.volumes, interface_placer.config,
|
convert_lines_to_internal(interface_placer.volumes, interface_placer.config,
|
||||||
@ -1354,7 +1374,8 @@ void sample_overhang_area(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
interface_placer.add_roofs(std::move(added_roofs), layer_idx, dtt_roof);
|
added_roofs.erase(added_roofs.begin() + dtt_roof, added_roofs.end());
|
||||||
|
interface_placer.add_roofs(std::move(added_roofs), layer_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overhang_lines.empty()) {
|
if (overhang_lines.empty()) {
|
||||||
@ -1364,7 +1385,7 @@ void sample_overhang_area(
|
|||||||
bool supports_roof = dtt_roof > 0;
|
bool supports_roof = dtt_roof > 0;
|
||||||
bool continuous_tips = ! supports_roof && large_horizontal_roof;
|
bool continuous_tips = ! supports_roof && large_horizontal_roof;
|
||||||
Polylines polylines = ensure_maximum_distance_polyline(
|
Polylines polylines = ensure_maximum_distance_polyline(
|
||||||
generate_support_infill_lines(overhang_area, support_params, supports_roof, layer_idx - layer_generation_dtt,
|
generate_support_infill_lines(overhang_area, interface_placer.support_parameters, supports_roof, layer_idx - layer_generation_dtt,
|
||||||
supports_roof ? mesh_group_settings.support_roof_line_distance : mesh_group_settings.support_tree_branch_distance),
|
supports_roof ? mesh_group_settings.support_roof_line_distance : mesh_group_settings.support_tree_branch_distance),
|
||||||
continuous_tips ? interface_placer.config.min_radius / 2 : connect_length, 1);
|
continuous_tips ? interface_placer.config.min_radius / 2 : connect_length, 1);
|
||||||
size_t point_count = 0;
|
size_t point_count = 0;
|
||||||
@ -1388,9 +1409,10 @@ void sample_overhang_area(
|
|||||||
overhang_lines = convert_lines_to_internal(interface_placer.volumes, interface_placer.config, polylines, layer_idx - dtt_roof);
|
overhang_lines = convert_lines_to_internal(interface_placer.volumes, interface_placer.config, polylines, layer_idx - dtt_roof);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(dtt_roof <= layer_idx);
|
||||||
if (int(dtt_roof) >= layer_idx && large_horizontal_roof)
|
if (int(dtt_roof) >= layer_idx && large_horizontal_roof)
|
||||||
// reached buildplate
|
// Reached buildplate when generating contact, interface and base interface layers.
|
||||||
interface_placer.add_roof_build_plate(std::move(overhang_area));
|
interface_placer.add_roof_build_plate(std::move(overhang_area), dtt_roof);
|
||||||
else {
|
else {
|
||||||
// normal trees have to be generated
|
// normal trees have to be generated
|
||||||
const bool roof_enabled = num_support_roof_layers > 0;
|
const bool roof_enabled = num_support_roof_layers > 0;
|
||||||
@ -1401,8 +1423,8 @@ void sample_overhang_area(
|
|||||||
layer_idx - dtt_roof,
|
layer_idx - dtt_roof,
|
||||||
// Remaining roof tip layers.
|
// Remaining roof tip layers.
|
||||||
interface_placer.force_tip_to_roof ? num_support_roof_layers - dtt_roof : 0,
|
interface_placer.force_tip_to_roof ? num_support_roof_layers - dtt_roof : 0,
|
||||||
// Supports roof already?
|
// Supports roof already? How many roof layers were already produced above these tips?
|
||||||
dtt_roof > 0,
|
dtt_roof,
|
||||||
// Don't move until the following distance to top is reached.
|
// Don't move until the following distance to top is reached.
|
||||||
roof_enabled ? num_support_roof_layers - dtt_roof : 0);
|
roof_enabled ? num_support_roof_layers - dtt_roof : 0);
|
||||||
}
|
}
|
||||||
@ -1424,7 +1446,8 @@ static void generate_initial_areas(
|
|||||||
const std::vector<Polygons> &overhangs,
|
const std::vector<Polygons> &overhangs,
|
||||||
std::vector<SupportElements> &move_bounds,
|
std::vector<SupportElements> &move_bounds,
|
||||||
SupportGeneratorLayersPtr &top_contacts,
|
SupportGeneratorLayersPtr &top_contacts,
|
||||||
SupportGeneratorLayersPtr &top_interface_layers,
|
SupportGeneratorLayersPtr &top_interfaces,
|
||||||
|
SupportGeneratorLayersPtr &top_base_interfaces,
|
||||||
SupportGeneratorLayerStorage &layer_storage,
|
SupportGeneratorLayerStorage &layer_storage,
|
||||||
std::function<void()> throw_on_cancel)
|
std::function<void()> throw_on_cancel)
|
||||||
{
|
{
|
||||||
@ -1462,7 +1485,7 @@ static void generate_initial_areas(
|
|||||||
;
|
;
|
||||||
const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers;
|
const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers;
|
||||||
const bool roof_enabled = num_support_roof_layers > 0;
|
const bool roof_enabled = num_support_roof_layers > 0;
|
||||||
const bool force_tip_to_roof = sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area && roof_enabled;
|
const bool force_tip_to_roof = roof_enabled && sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area;
|
||||||
// cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point
|
// cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point
|
||||||
// may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang
|
// may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang
|
||||||
// does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it.
|
// does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it.
|
||||||
@ -1496,9 +1519,9 @@ static void generate_initial_areas(
|
|||||||
raw_overhangs.push_back({ layer_idx, &overhangs[overhang_idx] });
|
raw_overhangs.push_back({ layer_idx, &overhangs[overhang_idx] });
|
||||||
}
|
}
|
||||||
|
|
||||||
InterfacePlacer interface_placer{ print_object.slicing_parameters(), volumes, config, force_tip_to_roof, num_support_layers,
|
InterfacePlacer interface_placer{ print_object.slicing_parameters(), support_params, volumes, config, force_tip_to_roof, num_support_layers,
|
||||||
// Outputs
|
// Outputs
|
||||||
move_bounds, layer_storage, top_contacts };
|
move_bounds, layer_storage, top_contacts, top_interfaces, top_base_interfaces };
|
||||||
|
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, raw_overhangs.size()),
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, raw_overhangs.size()),
|
||||||
[&volumes, &config, &raw_overhangs, &mesh_group_settings, &support_params,
|
[&volumes, &config, &raw_overhangs, &mesh_group_settings, &support_params,
|
||||||
@ -1613,7 +1636,7 @@ static void generate_initial_areas(
|
|||||||
//check_self_intersections(overhang_regular, "overhang_regular3");
|
//check_self_intersections(overhang_regular, "overhang_regular3");
|
||||||
for (ExPolygon &roof_part : union_ex(overhang_roofs)) {
|
for (ExPolygon &roof_part : union_ex(overhang_roofs)) {
|
||||||
sample_overhang_area(to_polygons(std::move(roof_part)), true, layer_idx, num_support_roof_layers, connect_length,
|
sample_overhang_area(to_polygons(std::move(roof_part)), true, layer_idx, num_support_roof_layers, connect_length,
|
||||||
mesh_group_settings, support_params, interface_placer);
|
mesh_group_settings, interface_placer);
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1623,9 +1646,8 @@ static void generate_initial_areas(
|
|||||||
remove_small(overhang_regular, mesh_group_settings.minimum_support_area);
|
remove_small(overhang_regular, mesh_group_settings.minimum_support_area);
|
||||||
for (ExPolygon &support_part : union_ex(overhang_regular)) {
|
for (ExPolygon &support_part : union_ex(overhang_regular)) {
|
||||||
sample_overhang_area(to_polygons(std::move(support_part)),
|
sample_overhang_area(to_polygons(std::move(support_part)),
|
||||||
// Don't
|
|
||||||
false, layer_idx, num_support_roof_layers, connect_length,
|
false, layer_idx, num_support_roof_layers, connect_length,
|
||||||
mesh_group_settings, support_params, interface_placer);
|
mesh_group_settings, interface_placer);
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3392,7 +3414,7 @@ static void finalize_interface_and_support_areas(
|
|||||||
//FIXME subtract the wipe tower
|
//FIXME subtract the wipe tower
|
||||||
append(floor_layer, intersection(layer_outset, overhangs[sample_layer]));
|
append(floor_layer, intersection(layer_outset, overhangs[sample_layer]));
|
||||||
if (layers_below < config.support_bottom_layers)
|
if (layers_below < config.support_bottom_layers)
|
||||||
layers_below = std::min(layers_below + config.performance_interface_skip_layers, config.support_bottom_layers);
|
layers_below = std::min(layers_below + 1, config.support_bottom_layers);
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -4139,11 +4161,20 @@ static void organic_smooth_branches_avoid_collisions(
|
|||||||
#endif // TREE_SUPPORT_ORGANIC_NUDGE_NEW
|
#endif // TREE_SUPPORT_ORGANIC_NUDGE_NEW
|
||||||
|
|
||||||
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
|
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
|
||||||
static std::vector<Polygons> draw_branches(
|
static void draw_branches(
|
||||||
PrintObject &print_object,
|
PrintObject &print_object,
|
||||||
TreeModelVolumes &volumes,
|
TreeModelVolumes &volumes,
|
||||||
const TreeSupportSettings &config,
|
const TreeSupportSettings &config,
|
||||||
std::vector<SupportElements> &move_bounds,
|
std::vector<SupportElements> &move_bounds,
|
||||||
|
|
||||||
|
// I/O:
|
||||||
|
SupportGeneratorLayersPtr &bottom_contacts,
|
||||||
|
SupportGeneratorLayersPtr &top_contacts,
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
SupportGeneratorLayersPtr &intermediate_layers,
|
||||||
|
SupportGeneratorLayerStorage &layer_storage,
|
||||||
|
|
||||||
std::function<void()> throw_on_cancel)
|
std::function<void()> throw_on_cancel)
|
||||||
{
|
{
|
||||||
// All SupportElements are put into a layer independent storage to improve parallelization.
|
// All SupportElements are put into a layer independent storage to improve parallelization.
|
||||||
@ -4211,7 +4242,7 @@ static std::vector<Polygons> draw_branches(
|
|||||||
|
|
||||||
struct Slice {
|
struct Slice {
|
||||||
Polygons polygons;
|
Polygons polygons;
|
||||||
Polygons bottom_interfaces;
|
Polygons bottom_contacts;
|
||||||
size_t num_branches{ 0 };
|
size_t num_branches{ 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4316,6 +4347,7 @@ static std::vector<Polygons> draw_branches(
|
|||||||
[&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
|
[&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
|
||||||
indexed_triangle_set partial_mesh;
|
indexed_triangle_set partial_mesh;
|
||||||
std::vector<float> slice_z;
|
std::vector<float> slice_z;
|
||||||
|
std::vector<Polygons> bottom_contacts;
|
||||||
for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) {
|
for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) {
|
||||||
Tree &tree = trees[tree_id];
|
Tree &tree = trees[tree_id];
|
||||||
for (const Branch &branch : tree.branches) {
|
for (const Branch &branch : tree.branches) {
|
||||||
@ -4335,19 +4367,26 @@ static std::vector<Polygons> draw_branches(
|
|||||||
slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
|
slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
|
||||||
}
|
}
|
||||||
std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
|
std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
|
||||||
|
bottom_contacts.clear();
|
||||||
//FIXME parallelize?
|
//FIXME parallelize?
|
||||||
for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++ i)
|
for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++ i)
|
||||||
slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); //FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
|
slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); //FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
|
||||||
|
|
||||||
size_t num_empty = 0;
|
size_t num_empty = 0;
|
||||||
if (layer_begin > 0 && branch.has_root && ! branch.path.front()->state.to_model_gracious && ! slices.front().empty()) {
|
if (slices.front().empty()) {
|
||||||
|
// Some of the initial layers are empty.
|
||||||
|
num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
|
||||||
|
} else {
|
||||||
|
if (branch.has_root) {
|
||||||
|
if (branch.path.front()->state.to_model_gracious) {
|
||||||
|
if (config.settings.support_floor_layers > 0)
|
||||||
|
//FIXME one may just take the whole tree slice as bottom interface.
|
||||||
|
bottom_contacts.emplace_back(intersection_clipped(slices.front(), volumes.getPlaceableAreas(0, layer_begin, [] {})));
|
||||||
|
} else if (layer_begin > 0) {
|
||||||
// Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something.
|
// Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something.
|
||||||
struct BottomExtraSlice {
|
struct BottomExtraSlice {
|
||||||
Polygons polygons;
|
Polygons polygons;
|
||||||
Polygons supported;
|
|
||||||
Polygons bottom_interfaces;
|
|
||||||
double area;
|
double area;
|
||||||
double supported_area;
|
|
||||||
};
|
};
|
||||||
std::vector<BottomExtraSlice> bottom_extra_slices;
|
std::vector<BottomExtraSlice> bottom_extra_slices;
|
||||||
Polygons rest_support;
|
Polygons rest_support;
|
||||||
@ -4366,32 +4405,33 @@ static std::vector<Polygons> draw_branches(
|
|||||||
if (rest_support_area < support_area_stop)
|
if (rest_support_area < support_area_stop)
|
||||||
// Don't propagate a fraction of the tree contact surface.
|
// Don't propagate a fraction of the tree contact surface.
|
||||||
break;
|
break;
|
||||||
// Measure how much the rest_support is actually supported.
|
bottom_extra_slices.push_back({ rest_support, rest_support_area });
|
||||||
/*
|
|
||||||
Polygons supported = intersection_clipped(rest_support, volumes.getPlaceableAreas(0, layer_idx, []{}));
|
|
||||||
double supported_area = area(supported);
|
|
||||||
printf("Supported area: %d, %lf\n", layer_idx, supported_area);
|
|
||||||
*/
|
|
||||||
Polygons supported;
|
|
||||||
double supported_area;
|
|
||||||
bottom_extra_slices.push_back({ rest_support, std::move(supported), {}, rest_support_area, supported_area });
|
|
||||||
}
|
}
|
||||||
// Now remove those bottom slices that are not supported at all.
|
// Now remove those bottom slices that are not supported at all.
|
||||||
while (! bottom_extra_slices.empty()) {
|
while (! bottom_extra_slices.empty()) {
|
||||||
Polygons bottom_interfaces = intersection_clipped(bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}));
|
Polygons this_bottom_contacts = intersection_clipped(
|
||||||
if (area(bottom_interfaces) < support_area_min)
|
bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}));
|
||||||
|
if (area(this_bottom_contacts) < support_area_min)
|
||||||
bottom_extra_slices.pop_back();
|
bottom_extra_slices.pop_back();
|
||||||
else {
|
else if (config.settings.support_floor_layers > 0)
|
||||||
bottom_extra_slices.back().bottom_interfaces = std::move(bottom_interfaces);
|
bottom_contacts.emplace_back(std::move(this_bottom_contacts));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (config.settings.support_floor_layers > 0)
|
||||||
|
for (int i = int(bottom_extra_slices.size()) - 2; i >= 0; -- i)
|
||||||
|
bottom_contacts.emplace_back(
|
||||||
|
intersection_clipped(bottom_extra_slices[i].polygons, volumes.getPlaceableAreas(0, layer_begin - i - 1, [] {})));
|
||||||
layer_begin -= LayerIndex(bottom_extra_slices.size());
|
layer_begin -= LayerIndex(bottom_extra_slices.size());
|
||||||
slices.insert(slices.begin(), bottom_extra_slices.size(), {});
|
slices.insert(slices.begin(), bottom_extra_slices.size(), {});
|
||||||
size_t i = 0;
|
auto it_dst = slices.begin();
|
||||||
for (auto it = bottom_extra_slices.rbegin(); it != bottom_extra_slices.rend(); ++it, ++i)
|
for (auto it_src = bottom_extra_slices.rbegin(); it_src != bottom_extra_slices.rend(); ++ it_src)
|
||||||
slices[i] = std::move(it->polygons);
|
*it_dst ++ = std::move(it_src->polygons);
|
||||||
} else
|
}
|
||||||
num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
|
}
|
||||||
|
if (branch.has_tip) {
|
||||||
|
// Add top slices to top contacts / interfaces / base interfaces.
|
||||||
|
//slices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
layer_begin += LayerIndex(num_empty);
|
layer_begin += LayerIndex(num_empty);
|
||||||
while (! slices.empty() && slices.back().empty()) {
|
while (! slices.empty() && slices.back().empty()) {
|
||||||
@ -4414,13 +4454,20 @@ static std::vector<Polygons> draw_branches(
|
|||||||
tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {});
|
tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {});
|
||||||
tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {});
|
tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {});
|
||||||
layer_begin -= LayerIndex(num_empty);
|
layer_begin -= LayerIndex(num_empty);
|
||||||
for (LayerIndex i = layer_begin; i != layer_end; ++ i)
|
for (LayerIndex i = layer_begin; i != layer_end; ++ i) {
|
||||||
if (Polygons &src = slices[i - layer_begin]; ! src.empty()) {
|
int j = i - layer_begin;
|
||||||
|
if (Polygons &src = slices[j]; ! src.empty()) {
|
||||||
Slice &dst = tree.slices[i - new_begin];
|
Slice &dst = tree.slices[i - new_begin];
|
||||||
if (++ dst.num_branches > 1)
|
if (++ dst.num_branches > 1) {
|
||||||
append(dst.polygons, std::move(src));
|
append(dst.polygons, std::move(src));
|
||||||
else
|
if (j < bottom_contacts.size())
|
||||||
|
append(dst.bottom_contacts, std::move(bottom_contacts[j]));
|
||||||
|
} else {
|
||||||
dst.polygons = std::move(std::move(src));
|
dst.polygons = std::move(std::move(src));
|
||||||
|
if (j < bottom_contacts.size())
|
||||||
|
dst.bottom_contacts = std::move(bottom_contacts[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tree.first_layer_id = new_begin;
|
tree.first_layer_id = new_begin;
|
||||||
}
|
}
|
||||||
@ -4435,6 +4482,7 @@ static std::vector<Polygons> draw_branches(
|
|||||||
for (Slice &slice : tree.slices)
|
for (Slice &slice : tree.slices)
|
||||||
if (slice.num_branches > 1) {
|
if (slice.num_branches > 1) {
|
||||||
slice.polygons = union_(slice.polygons);
|
slice.polygons = union_(slice.polygons);
|
||||||
|
slice.bottom_contacts = union_(slice.bottom_contacts);
|
||||||
slice.num_branches = 1;
|
slice.num_branches = 1;
|
||||||
}
|
}
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
@ -4452,25 +4500,55 @@ static std::vector<Polygons> draw_branches(
|
|||||||
for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i)
|
for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i)
|
||||||
if (Slice &src = tree.slices[i - tree.first_layer_id]; ! src.polygons.empty()) {
|
if (Slice &src = tree.slices[i - tree.first_layer_id]; ! src.polygons.empty()) {
|
||||||
Slice &dst = slices[i];
|
Slice &dst = slices[i];
|
||||||
if (++ dst.num_branches > 1)
|
if (++ dst.num_branches > 1) {
|
||||||
append(dst.polygons, std::move(src.polygons));
|
append(dst.polygons, std::move(src.polygons));
|
||||||
else
|
append(dst.bottom_contacts, std::move(src.bottom_contacts));
|
||||||
|
} else {
|
||||||
dst.polygons = std::move(src.polygons);
|
dst.polygons = std::move(src.polygons);
|
||||||
|
dst.bottom_contacts = std::move(src.bottom_contacts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Polygons> support_layer_storage(move_bounds.size());
|
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, std::min(move_bounds.size(), slices.size()), 1),
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, std::min(move_bounds.size(), slices.size()), 1),
|
||||||
[&slices, &support_layer_storage, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
|
[&print_object, &config, &slices, &bottom_contacts, &top_contacts, &intermediate_layers, &layer_storage, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
|
||||||
for (size_t slice_id = range.begin(); slice_id < range.end(); ++ slice_id) {
|
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||||
Slice &slice = slices[slice_id];
|
Slice &slice = slices[layer_idx];
|
||||||
support_layer_storage[slice_id] = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons);
|
assert(intermediate_layers[layer_idx] == nullptr);
|
||||||
|
Polygons base_layer_polygons = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons);
|
||||||
|
Polygons bottom_contact_polygons = slice.num_branches > 1 ? union_(slice.bottom_contacts) : std::move(slice.bottom_contacts);
|
||||||
|
|
||||||
|
if (! base_layer_polygons.empty()) {
|
||||||
|
// Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned.
|
||||||
|
base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50);
|
||||||
|
//smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
|
||||||
|
// simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution.
|
||||||
|
base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled<double>(0.03), double(config.resolution)), polygons_strictly_simple);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract top contact layer polygons from support base.
|
||||||
|
SupportGeneratorLayer *top_contact_layer = top_contacts[layer_idx];
|
||||||
|
if (top_contact_layer && ! top_contact_layer->polygons.empty() && ! base_layer_polygons.empty()) {
|
||||||
|
base_layer_polygons = diff(base_layer_polygons, top_contact_layer->polygons);
|
||||||
|
if (! bottom_contact_polygons.empty())
|
||||||
|
//FIXME it may be better to clip bottom contacts with top contacts first after they are propagated to produce interface layers.
|
||||||
|
bottom_contact_polygons = diff(bottom_contact_polygons, top_contact_layer->polygons);
|
||||||
|
}
|
||||||
|
if (! bottom_contact_polygons.empty()) {
|
||||||
|
base_layer_polygons = diff(base_layer_polygons, bottom_contact_polygons);
|
||||||
|
SupportGeneratorLayer *bottom_contact_layer = bottom_contacts[layer_idx] = &layer_allocate(
|
||||||
|
layer_storage, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx);
|
||||||
|
bottom_contact_layer->polygons = std::move(bottom_contact_polygons);
|
||||||
|
}
|
||||||
|
if (! base_layer_polygons.empty()) {
|
||||||
|
SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(
|
||||||
|
layer_storage, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx);
|
||||||
|
base_layer->polygons = union_(base_layer_polygons);
|
||||||
|
}
|
||||||
|
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
}, tbb::simple_partitioner());
|
}, tbb::simple_partitioner());
|
||||||
|
|
||||||
//FIXME simplify!
|
|
||||||
return support_layer_storage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -4538,9 +4616,12 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
|
|||||||
SupportGeneratorLayerStorage layer_storage;
|
SupportGeneratorLayerStorage layer_storage;
|
||||||
SupportGeneratorLayersPtr top_contacts;
|
SupportGeneratorLayersPtr top_contacts;
|
||||||
SupportGeneratorLayersPtr bottom_contacts;
|
SupportGeneratorLayersPtr bottom_contacts;
|
||||||
SupportGeneratorLayersPtr top_interface_layers;
|
SupportGeneratorLayersPtr interface_layers;
|
||||||
|
SupportGeneratorLayersPtr base_interface_layers;
|
||||||
SupportGeneratorLayersPtr intermediate_layers;
|
SupportGeneratorLayersPtr intermediate_layers;
|
||||||
|
|
||||||
|
SupportParameters support_params(print_object);
|
||||||
|
|
||||||
if (size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
|
if (size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
|
||||||
num_support_layers > 0) {
|
num_support_layers > 0) {
|
||||||
|
|
||||||
@ -4550,13 +4631,15 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
|
|||||||
std::vector<SupportElements> move_bounds(num_support_layers);
|
std::vector<SupportElements> move_bounds(num_support_layers);
|
||||||
|
|
||||||
// ### Place tips of the support tree
|
// ### Place tips of the support tree
|
||||||
top_contacts .assign(num_support_layers, nullptr);
|
|
||||||
bottom_contacts .assign(num_support_layers, nullptr);
|
bottom_contacts .assign(num_support_layers, nullptr);
|
||||||
top_interface_layers.assign(num_support_layers, nullptr);
|
top_contacts .assign(num_support_layers, nullptr);
|
||||||
|
interface_layers .assign(num_support_layers, nullptr);
|
||||||
|
base_interface_layers.assign(num_support_layers, nullptr);
|
||||||
intermediate_layers .assign(num_support_layers, nullptr);
|
intermediate_layers .assign(num_support_layers, nullptr);
|
||||||
|
|
||||||
for (size_t mesh_idx : processing.second)
|
for (size_t mesh_idx : processing.second)
|
||||||
generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, move_bounds, top_contacts, top_interface_layers, layer_storage, throw_on_cancel);
|
generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs,
|
||||||
|
move_bounds, top_contacts, interface_layers, base_interface_layers, layer_storage, throw_on_cancel);
|
||||||
auto t_gen = std::chrono::high_resolution_clock::now();
|
auto t_gen = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
#ifdef TREESUPPORT_DEBUG_SVG
|
#ifdef TREESUPPORT_DEBUG_SVG
|
||||||
@ -4587,12 +4670,24 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
|
|||||||
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
|
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
|
||||||
else {
|
else {
|
||||||
assert(print_object.config().support_material_style == smsOrganic);
|
assert(print_object.config().support_material_style == smsOrganic);
|
||||||
std::vector<Polygons> support_layer_storage = draw_branches(*print.get_object(processing.second.front()), volumes, config, move_bounds, throw_on_cancel);
|
draw_branches(
|
||||||
std::vector<Polygons> support_roof_storage(support_layer_storage.size());
|
*print.get_object(processing.second.front()), volumes, config, move_bounds,
|
||||||
finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage,
|
bottom_contacts, top_contacts, intermediate_layers, layer_storage,
|
||||||
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
|
throw_on_cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto remove_undefined_layers = [](SupportGeneratorLayersPtr& layers) {
|
||||||
|
layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end());
|
||||||
|
};
|
||||||
|
remove_undefined_layers(bottom_contacts);
|
||||||
|
remove_undefined_layers(top_contacts);
|
||||||
|
remove_undefined_layers(interface_layers);
|
||||||
|
remove_undefined_layers(base_interface_layers);
|
||||||
|
remove_undefined_layers(intermediate_layers);
|
||||||
|
|
||||||
|
std::tie(interface_layers, base_interface_layers) = generate_interface_layers(print_object.config(), support_params,
|
||||||
|
bottom_contacts, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
|
||||||
|
|
||||||
auto t_draw = std::chrono::high_resolution_clock::now();
|
auto t_draw = std::chrono::high_resolution_clock::now();
|
||||||
auto dur_pre_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_precalc - t_start).count();
|
auto dur_pre_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_precalc - t_start).count();
|
||||||
auto dur_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_gen - t_precalc).count();
|
auto dur_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_gen - t_precalc).count();
|
||||||
@ -4618,18 +4713,10 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto remove_undefined_layers = [](SupportGeneratorLayersPtr &layers) {
|
|
||||||
layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end());
|
|
||||||
};
|
|
||||||
remove_undefined_layers(bottom_contacts);
|
|
||||||
remove_undefined_layers(top_contacts);
|
|
||||||
remove_undefined_layers(intermediate_layers);
|
|
||||||
|
|
||||||
// Produce the support G-code.
|
// Produce the support G-code.
|
||||||
// Used by both classic and tree supports.
|
// Used by both classic and tree supports.
|
||||||
SupportParameters support_params(print_object);
|
SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(),
|
||||||
SupportGeneratorLayersPtr interface_layers, base_interface_layers;
|
top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
|
||||||
SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
|
|
||||||
#if 1 //#ifdef SLIC3R_DEBUG
|
#if 1 //#ifdef SLIC3R_DEBUG
|
||||||
SupportGeneratorLayersPtr layers_sorted =
|
SupportGeneratorLayersPtr layers_sorted =
|
||||||
#endif // SLIC3R_DEBUG
|
#endif // SLIC3R_DEBUG
|
||||||
|
@ -364,10 +364,6 @@ public:
|
|||||||
* \brief Amount of layers distance required from the top of the model to the bottom of a support structure.
|
* \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;
|
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.
|
* \brief User specified angles for the support infill.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user