Organic supports: Refactoring for adding interfaces to tree tips.

This commit is contained in:
Vojtech Bubnik 2023-05-09 09:46:27 +02:00
parent c838fc92fc
commit 9d495f2413

View File

@ -997,11 +997,245 @@ inline SupportGeneratorLayer& layer_allocate(
return layer_initialize(layer, slicing_params, config, layer_idx);
}
using SupportElements = std::deque<SupportElement>;
// 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::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));
}
private:
// Outputs
SupportGeneratorLayerStorage &layer_storage;
SupportGeneratorLayersPtr &top_contacts;
SupportGeneratorLayersPtr &top_interfaces;
SupportGeneratorLayersPtr &top_base_interfaces;
// Mutexes, guards
std::mutex m_mutex_layer_storage;
};
class RichInterfacePlacer : public InterfacePlacer {
public:
RichInterfacePlacer(
const InterfacePlacer &interface_placer,
const TreeModelVolumes &volumes,
bool force_tip_to_roof,
size_t num_support_layers,
std::vector<SupportElements> &move_bounds)
:
InterfacePlacer(interface_placer),
volumes(volumes), force_tip_to_roof(force_tip_to_roof), move_bounds(move_bounds)
{
m_already_inserted.assign(num_support_layers, {});
this->min_xy_dist = this->config.xy_distance > this->config.xy_min_distance;
}
const TreeModelVolumes &volumes;
// Radius of the tree tip is large enough to be covered by an interface.
const bool force_tip_to_roof;
bool min_xy_dist;
public:
// called by sample_overhang_area()
void add_points_along_lines(
// Insert points (tree tips or top contact interfaces) along these lines.
LineInformations lines,
// Start at this layer.
LayerIndex insert_layer_idx,
// Insert this number of interface layers.
size_t roof_tip_layers,
// True if an interface is already generated above these lines.
size_t supports_roof_layers,
// The element tries to not move until this dtt is reached.
size_t dont_move_until)
{
validate_range(lines);
// Add tip area as roof (happens when minimum roof area > minimum tip area) if possible
size_t dtt_roof_tip;
for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; ++ dtt_roof_tip) {
size_t this_layer_idx = insert_layer_idx - dtt_roof_tip;
auto evaluateRoofWillGenerate = [&](const std::pair<Point, LineStatus> &p) {
//FIXME Vojtech: The circle is just shifted, it has a known size, the infill should fit all the time!
#if 0
Polygon roof_circle;
for (Point corner : base_circle)
roof_circle.points.emplace_back(p.first + corner * config.min_radius);
return !generate_support_infill_lines({ roof_circle }, config, true, insert_layer_idx - dtt_roof_tip, config.support_roof_line_distance).empty();
#else
return true;
#endif
};
{
std::pair<LineInformations, LineInformations> split =
// keep all lines that are still valid on the next layer
split_lines(lines, [this, this_layer_idx](const std::pair<Point, LineStatus> &p)
{ return evaluate_point_for_next_layer_function(volumes, config, this_layer_idx, p); });
LineInformations points = std::move(split.second);
// Not all roofs are guaranteed to actually generate lines, so filter these out and add them as points.
split = split_lines(split.first, evaluateRoofWillGenerate);
lines = std::move(split.first);
append(points, split.second);
// add all points that would not be valid
for (const LineInformation &line : points)
for (const std::pair<Point, LineStatus> &point_data : line)
add_point_as_influence_area(point_data, this_layer_idx,
// don't move until
roof_tip_layers - dtt_roof_tip,
// supports roof
dtt_roof_tip + supports_roof_layers > 0,
// disable ovalization
false);
}
// add all tips as roof to the roof storage
Polygons new_roofs;
for (const LineInformation &line : lines)
//FIXME sweep the tip radius along the line?
for (const std::pair<Point, LineStatus> &p : line) {
Polygon roof_circle{ m_base_circle };
roof_circle.scale(config.min_radius / m_base_radius);
roof_circle.translate(p.first);
new_roofs.emplace_back(std::move(roof_circle));
}
this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers);
}
for (const LineInformation &line : lines) {
// If a line consists of enough tips, the assumption is that it is not a single tip, but part of a simulated support pattern.
// Ovalisation should be disabled for these to improve the quality of the lines when tip_diameter=line_width
bool disable_ovalistation = config.min_radius < 3 * config.support_line_width && roof_tip_layers == 0 && dtt_roof_tip == 0 && line.size() > 5;
for (const std::pair<Point, LineStatus> &point_data : line)
add_point_as_influence_area(point_data, insert_layer_idx - dtt_roof_tip,
// don't move until
dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0,
// supports roof
dtt_roof_tip + supports_roof_layers > 0,
disable_ovalistation);
}
}
private:
// 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)
{
bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE;
bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE;
bool safe_radius = p.second == LineStatus::TO_BP_SAFE || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE;
if (! config.support_rests_on_model && ! to_bp) {
BOOST_LOG_TRIVIAL(warning) << "Tried to add an invalid support point";
tree_supports_show_error("Unable to add tip. Some overhang may not be supported correctly."sv, true);
return;
}
Polygons circle{ m_base_circle };
circle.front().translate(p.first);
{
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)) {
// 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);
static constexpr const size_t dtt = 0;
SupportElementState state;
state.target_height = insert_layer;
state.target_position = p.first;
state.next_position = p.first;
state.layer_idx = insert_layer;
state.effective_radius_height = dtt;
state.to_buildplate = to_bp;
state.distance_to_top = dtt;
state.result_on_layer = p.first;
assert(state.result_on_layer_is_set());
state.increased_to_model_radius = 0;
state.to_model_gracious = gracious;
state.elephant_foot_increases = 0;
state.use_min_xy_dist = min_xy_dist;
state.supports_roof = roof;
state.dont_move_until = dont_move_until;
state.can_use_safe_radius = safe_radius;
state.missing_roof_layers = force_tip_to_roof ? dont_move_until : 0;
state.skip_ovalisation = skip_ovalisation;
move_bounds[insert_layer].emplace_back(state, std::move(circle));
}
}
}
// Outputs
std::vector<SupportElements> &move_bounds;
// 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::vector<std::unordered_set<Point, PointHash>> m_already_inserted;
};
int generate_raft_contact(
const PrintObject &print_object,
const TreeSupportSettings &config,
SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayerStorage &layer_storage)
InterfacePlacer &interface_placer)
{
int raft_contact_layer_idx = -1;
if (print_object.has_raft() && print_object.layer_count() > 0) {
@ -1012,17 +1246,13 @@ int generate_raft_contact(
while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON)
-- raft_contact_layer_idx;
// Create the raft contact layer.
SupportGeneratorLayer &raft_contact_layer = layer_allocate_unguarded(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx);
top_contacts[raft_contact_layer_idx] = &raft_contact_layer;
const ExPolygons &lslices = print_object.get_layer(0)->lslices;
double expansion = print_object.config().raft_expansion.value;
raft_contact_layer.polygons = expansion > 0 ? expand(lslices, scaled<float>(expansion)) : to_polygons(lslices);
interface_placer.add_roof_unguarded(expansion > 0 ? expand(lslices, scaled<float>(expansion)) : to_polygons(lslices), raft_contact_layer_idx, 0);
}
return raft_contact_layer_idx;
}
using SupportElements = std::deque<SupportElement>;
void finalize_raft_contact(
const PrintObject &print_object,
const int raft_contact_layer_idx,
@ -1076,215 +1306,6 @@ void finalize_raft_contact(
}
}
// Used by generate_initial_areas() in parallel by multiple layers.
class InterfacePlacer {
public:
InterfacePlacer(
const SlicingParameters &slicing_parameters,
const SupportParameters &support_parameters,
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, {});
this->min_xy_dist = config.xy_distance > config.xy_min_distance;
}
const SlicingParameters &slicing_parameters;
const SupportParameters &support_parameters;
const TreeModelVolumes &volumes;
const TreeSupportSettings &config;
// Radius of the tree tip is large enough to be covered by an interface.
const bool force_tip_to_roof;
bool min_xy_dist;
public:
// called by sample_overhang_area()
// 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);
}
}
// 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));
}
// called by sample_overhang_area()
void add_points_along_lines(
// Insert points (tree tips or top contact interfaces) along these lines.
LineInformations lines,
// Start at this layer.
LayerIndex insert_layer_idx,
// Insert this number of interface layers.
size_t roof_tip_layers,
// True if an interface is already generated above these lines.
size_t supports_roof_layers,
// The element tries to not move until this dtt is reached.
size_t dont_move_until)
{
validate_range(lines);
// Add tip area as roof (happens when minimum roof area > minimum tip area) if possible
size_t dtt_roof_tip;
for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; ++ dtt_roof_tip) {
size_t this_layer_idx = insert_layer_idx - dtt_roof_tip;
auto evaluateRoofWillGenerate = [&](const std::pair<Point, LineStatus> &p) {
//FIXME Vojtech: The circle is just shifted, it has a known size, the infill should fit all the time!
#if 0
Polygon roof_circle;
for (Point corner : base_circle)
roof_circle.points.emplace_back(p.first + corner * config.min_radius);
return !generate_support_infill_lines({ roof_circle }, config, true, insert_layer_idx - dtt_roof_tip, config.support_roof_line_distance).empty();
#else
return true;
#endif
};
{
std::pair<LineInformations, LineInformations> split =
// keep all lines that are still valid on the next layer
split_lines(lines, [this, this_layer_idx](const std::pair<Point, LineStatus> &p)
{ return evaluate_point_for_next_layer_function(volumes, config, this_layer_idx, p); });
LineInformations points = std::move(split.second);
// Not all roofs are guaranteed to actually generate lines, so filter these out and add them as points.
split = split_lines(split.first, evaluateRoofWillGenerate);
lines = std::move(split.first);
append(points, split.second);
// add all points that would not be valid
for (const LineInformation &line : points)
for (const std::pair<Point, LineStatus> &point_data : line)
add_point_as_influence_area(point_data, this_layer_idx,
// don't move until
roof_tip_layers - dtt_roof_tip,
// supports roof
dtt_roof_tip > 0,
// disable ovalization
false);
}
// add all tips as roof to the roof storage
Polygons new_roofs;
for (const LineInformation &line : lines)
//FIXME sweep the tip radius along the line?
for (const std::pair<Point, LineStatus> &p : line) {
Polygon roof_circle{ m_base_circle };
roof_circle.scale(config.min_radius / m_base_radius);
roof_circle.translate(p.first);
new_roofs.emplace_back(std::move(roof_circle));
}
this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers);
}
for (const LineInformation &line : lines) {
// If a line consists of enough tips, the assumption is that it is not a single tip, but part of a simulated support pattern.
// Ovalisation should be disabled for these to improve the quality of the lines when tip_diameter=line_width
bool disable_ovalistation = config.min_radius < 3 * config.support_line_width && roof_tip_layers == 0 && dtt_roof_tip == 0 && line.size() > 5;
for (const std::pair<Point, LineStatus> &point_data : line)
add_point_as_influence_area(point_data, insert_layer_idx - dtt_roof_tip,
// don't move until
dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0,
// supports roof
dtt_roof_tip + supports_roof_layers > 0,
disable_ovalistation);
}
}
private:
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::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)
{
bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE;
bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE;
bool safe_radius = p.second == LineStatus::TO_BP_SAFE || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE;
if (! config.support_rests_on_model && ! to_bp) {
BOOST_LOG_TRIVIAL(warning) << "Tried to add an invalid support point";
tree_supports_show_error("Unable to add tip. Some overhang may not be supported correctly."sv, true);
return;
}
Polygons circle{ m_base_circle };
circle.front().translate(p.first);
{
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)) {
// 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);
static constexpr const size_t dtt = 0;
SupportElementState state;
state.target_height = insert_layer;
state.target_position = p.first;
state.next_position = p.first;
state.layer_idx = insert_layer;
state.effective_radius_height = dtt;
state.to_buildplate = to_bp;
state.distance_to_top = dtt;
state.result_on_layer = p.first;
assert(state.result_on_layer_is_set());
state.increased_to_model_radius = 0;
state.to_model_gracious = gracious;
state.elephant_foot_increases = 0;
state.use_min_xy_dist = min_xy_dist;
state.supports_roof = roof;
state.dont_move_until = dont_move_until;
state.can_use_safe_radius = safe_radius;
state.missing_roof_layers = force_tip_to_roof ? dont_move_until : 0;
state.skip_ovalisation = skip_ovalisation;
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
// 1) Maximum num_support_roof_layers roof (top interface & contact) layers.
@ -1309,7 +1330,7 @@ void sample_overhang_area(
// Configuration classes
const TreeSupportMeshGroupSettings &mesh_group_settings,
// Configuration & Output
InterfacePlacer &interface_placer)
RichInterfacePlacer &interface_placer)
{
// Assumption is that roof will support roof further up to avoid a lot of unnecessary branches. Each layer down it is checked whether the roof area
// is still large enough to be a roof and aborted as soon as it is not. This part was already reworked a few times, and there could be an argument
@ -1447,17 +1468,11 @@ static void generate_initial_areas(
const TreeSupportSettings &config,
const std::vector<Polygons> &overhangs,
std::vector<SupportElements> &move_bounds,
SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &top_interfaces,
SupportGeneratorLayersPtr &top_base_interfaces,
SupportGeneratorLayerStorage &layer_storage,
InterfacePlacer &interface_placer,
std::function<void()> throw_on_cancel)
{
using AvoidanceType = TreeModelVolumes::AvoidanceType;
TreeSupportMeshGroupSettings mesh_group_settings(print_object);
SupportParameters support_params(print_object);
support_params.with_sheath = true;
support_params.support_density = 0;
// To ensure z_distance_top_layers are left empty between the overhang (zeroth empty layer), the support has to be added z_distance_top_layers+1 layers below
const size_t z_distance_delta = config.z_distance_top_layers + 1;
@ -1487,7 +1502,7 @@ static void generate_initial_areas(
;
const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers;
const bool roof_enabled = num_support_roof_layers > 0;
const bool force_tip_to_roof = roof_enabled && sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area;
const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.soluble_interface || 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
// 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.
@ -1513,7 +1528,7 @@ static void generate_initial_areas(
const size_t num_raft_layers = config.raft_layers.size();
const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1);
num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta)));
raft_contact_layer_idx = generate_raft_contact(print_object, config, top_contacts, layer_storage);
raft_contact_layer_idx = generate_raft_contact(print_object, config, interface_placer);
// Enumerate layers for which the support tips may be generated from overhangs above.
raw_overhangs.reserve(num_support_layers - first_support_layer);
for (size_t layer_idx = first_support_layer; layer_idx < num_support_layers; ++ layer_idx)
@ -1521,14 +1536,12 @@ static void generate_initial_areas(
raw_overhangs.push_back({ layer_idx, &overhangs[overhang_idx] });
}
InterfacePlacer interface_placer{ print_object.slicing_parameters(), support_params, volumes, config, force_tip_to_roof, num_support_layers,
// Outputs
move_bounds, layer_storage, top_contacts, top_interfaces, top_base_interfaces };
RichInterfacePlacer rich_interface_placer{ interface_placer, volumes, force_tip_to_roof, num_support_layers, move_bounds };
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,
min_xy_dist, force_tip_to_roof, roof_enabled, num_support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag,
&interface_placer, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
&rich_interface_placer, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
for (size_t raw_overhang_idx = range.begin(); raw_overhang_idx < range.end(); ++ raw_overhang_idx) {
size_t layer_idx = raw_overhangs[raw_overhang_idx].first;
const Polygons &overhang_raw = *raw_overhangs[raw_overhang_idx].second;
@ -1620,7 +1633,7 @@ static void generate_initial_areas(
LineInformations fresh_valid_points = convert_lines_to_internal(volumes, config, convert_internal_to_lines(split.second), layer_idx - lag_ctr);
validate_range(fresh_valid_points);
interface_placer.add_points_along_lines(fresh_valid_points, (force_tip_to_roof && lag_ctr <= num_support_roof_layers) ? num_support_roof_layers : 0, layer_idx - lag_ctr, false, roof_enabled ? num_support_roof_layers : 0);
rich_interface_placer.add_points_along_lines(fresh_valid_points, (force_tip_to_roof && lag_ctr <= num_support_roof_layers) ? num_support_roof_layers : 0, layer_idx - lag_ctr, false, roof_enabled ? num_support_roof_layers : 0);
}
}
#endif
@ -1638,7 +1651,7 @@ static void generate_initial_areas(
//check_self_intersections(overhang_regular, "overhang_regular3");
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,
mesh_group_settings, interface_placer);
mesh_group_settings, rich_interface_placer);
throw_on_cancel();
}
}
@ -1649,13 +1662,13 @@ static void generate_initial_areas(
for (ExPolygon &support_part : union_ex(overhang_regular)) {
sample_overhang_area(to_polygons(std::move(support_part)),
false, layer_idx, num_support_roof_layers, connect_length,
mesh_group_settings, interface_placer);
mesh_group_settings, rich_interface_placer);
throw_on_cancel();
}
}
});
finalize_raft_contact(print_object, raft_contact_layer_idx, top_contacts, move_bounds);
finalize_raft_contact(print_object, raft_contact_layer_idx, interface_placer.top_contacts_mutable(), move_bounds);
}
static unsigned int move_inside(const Polygons &polygons, Point &from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max())
@ -4172,6 +4185,7 @@ static void draw_branches(
// I/O:
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
InterfacePlacer &interface_placer,
// Output:
SupportGeneratorLayersPtr &intermediate_layers,
@ -4346,7 +4360,7 @@ static void draw_branches(
mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive;
tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size(), 1),
[&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, &interface_placer, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
indexed_triangle_set partial_mesh;
std::vector<float> slice_z;
std::vector<Polygons> bottom_contacts;
@ -4434,10 +4448,20 @@ static void draw_branches(
*it_dst ++ = std::move(it_src->polygons);
}
}
if (branch.has_tip) {
#if 0
//FIXME branch.has_tip seems to not be reliable.
if (branch.has_tip && interface_placer.support_parameters.has_top_contacts)
// Add top slices to top contacts / interfaces / base interfaces.
//slices;
}
for (int i = int(branch.path.size()) - 1; i >= 0; -- i) {
const SupportElement &el = *branch.path[i];
if (el.state.missing_roof_layers == 0)
break;
//FIXME Move or not?
interface_placer.add_roof(std::move(slices[int(slices.size()) - i - 1]), el.state.layer_idx,
interface_placer.support_parameters.num_top_interface_layers + 1 - el.state.missing_roof_layers);
}
#endif
}
layer_begin += LayerIndex(num_empty);
@ -4619,34 +4643,44 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
std::vector<Polygons> overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel);
// ### Precalculate avoidances, collision etc.
size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
bool has_support = num_support_layers > 0;
num_support_layers = std::max(num_support_layers, config.raft_layers.size());
SupportParameters support_params(print_object);
support_params.with_sheath = true;
support_params.support_density = 0;
SupportGeneratorLayerStorage layer_storage;
SupportGeneratorLayersPtr top_contacts;
SupportGeneratorLayersPtr bottom_contacts;
SupportGeneratorLayersPtr interface_layers;
SupportGeneratorLayersPtr base_interface_layers;
SupportGeneratorLayersPtr intermediate_layers;
SupportGeneratorLayersPtr intermediate_layers(num_support_layers, nullptr);
if (support_params.has_top_contacts)
top_contacts.assign(num_support_layers, nullptr);
if (support_params.has_bottom_contacts)
bottom_contacts.assign(num_support_layers, nullptr);
if (support_params.has_interfaces())
interface_layers.assign(num_support_layers, nullptr);
if (support_params.has_base_interfaces())
base_interface_layers.assign(num_support_layers, nullptr);
SupportParameters support_params(print_object);
if (size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
num_support_layers > 0) {
InterfacePlacer interface_placer{
print_object.slicing_parameters(), support_params, config,
// Outputs
layer_storage, top_contacts, interface_layers, base_interface_layers };
if (has_support) {
auto t_precalc = std::chrono::high_resolution_clock::now();
// value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in draw_areas
std::vector<SupportElements> move_bounds(num_support_layers);
// ### Place tips of the support tree
bottom_contacts .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);
for (size_t mesh_idx : processing.second)
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);
move_bounds, interface_placer, throw_on_cancel);
auto t_gen = std::chrono::high_resolution_clock::now();
#ifdef TREESUPPORT_DEBUG_SVG
@ -4679,7 +4713,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
assert(print_object.config().support_material_style == smsOrganic);
draw_branches(
*print.get_object(processing.second.front()), volumes, config, move_bounds,
bottom_contacts, top_contacts, intermediate_layers, layer_storage,
bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage,
throw_on_cancel);
}
@ -4713,12 +4747,9 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
// BOOST_LOG_TRIVIAL(error) << "Why ask questions when you already know the answer twice.\n (This is not a real bug, please dont report it.)";
move_bounds.clear();
} else {
top_contacts.assign(config.raft_layers.size(), nullptr);
if (generate_raft_contact(print_object, config, top_contacts, layer_storage) < 0)
// No raft.
continue;
}
} else if (generate_raft_contact(print_object, config, interface_placer) < 0)
// No raft.
continue;
// Produce the support G-code.
// Used by both classic and tree supports.