WIP Tree supports: It compiles and it produced first trees.
This commit is contained in:
parent
9e6871e5b8
commit
5868028a7e
6 changed files with 235 additions and 158 deletions
|
@ -137,7 +137,8 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialPattern)
|
|||
|
||||
static const t_config_enum_values s_keys_map_SupportMaterialStyle {
|
||||
{ "grid", smsGrid },
|
||||
{ "snug", smsSnug }
|
||||
{ "snug", smsSnug },
|
||||
{ "tree", smsTree }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle)
|
||||
|
||||
|
|
|
@ -573,71 +573,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
|
|||
// intermediate_layers.clear();
|
||||
// interface_layers.clear();
|
||||
|
||||
// Install support layers into the object.
|
||||
// A support layer installed on a PrintObject has a unique print_z.
|
||||
SupportGeneratorLayersPtr layers_sorted;
|
||||
layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size() + base_interface_layers.size());
|
||||
layers_append(layers_sorted, raft_layers);
|
||||
layers_append(layers_sorted, bottom_contacts);
|
||||
layers_append(layers_sorted, top_contacts);
|
||||
layers_append(layers_sorted, intermediate_layers);
|
||||
layers_append(layers_sorted, interface_layers);
|
||||
layers_append(layers_sorted, base_interface_layers);
|
||||
// Sort the layers lexicographically by a raising print_z and a decreasing height.
|
||||
std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; });
|
||||
int layer_id = 0;
|
||||
int layer_id_interface = 0;
|
||||
assert(object.support_layers().empty());
|
||||
for (size_t i = 0; i < layers_sorted.size();) {
|
||||
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
|
||||
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
|
||||
size_t j = i + 1;
|
||||
coordf_t zmax = layers_sorted[i]->print_z + EPSILON;
|
||||
for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ;
|
||||
// Assign an average print_z to the set of layers with nearly equal print_z.
|
||||
coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z);
|
||||
coordf_t height_min = layers_sorted[i]->height;
|
||||
bool empty = true;
|
||||
// For snug supports, layers where the direction of the support interface shall change are accounted for.
|
||||
size_t num_interfaces = 0;
|
||||
size_t num_top_contacts = 0;
|
||||
double top_contact_bottom_z = 0;
|
||||
for (size_t u = i; u < j; ++u) {
|
||||
SupportGeneratorLayer &layer = *layers_sorted[u];
|
||||
if (! layer.polygons.empty()) {
|
||||
empty = false;
|
||||
num_interfaces += one_of(layer.layer_type, support_types_interface);
|
||||
if (layer.layer_type == SupporLayerType::TopContact) {
|
||||
++ num_top_contacts;
|
||||
assert(num_top_contacts <= 1);
|
||||
// All top contact layers sharing this print_z shall also share bottom_z.
|
||||
//assert(num_top_contacts == 1 || (top_contact_bottom_z - layer.bottom_z) < EPSILON);
|
||||
top_contact_bottom_z = layer.bottom_z;
|
||||
}
|
||||
}
|
||||
layer.print_z = zavg;
|
||||
height_min = std::min(height_min, layer.height);
|
||||
}
|
||||
if (! empty) {
|
||||
// Here the upper_layer and lower_layer pointers are left to null at the support layers,
|
||||
// as they are never used. These pointers are candidates for removal.
|
||||
bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces;
|
||||
size_t this_layer_id_interface = layer_id_interface;
|
||||
if (this_layer_contacts_only) {
|
||||
// Find a supporting layer for its interface ID.
|
||||
for (auto it = object.support_layers().rbegin(); it != object.support_layers().rend(); ++ it)
|
||||
if (const SupportLayer &other_layer = **it; std::abs(other_layer.print_z - top_contact_bottom_z) < EPSILON) {
|
||||
// other_layer supports this top contact layer. Assign a different support interface direction to this layer
|
||||
// from the layer that supports it.
|
||||
this_layer_id_interface = other_layer.interface_id() + 1;
|
||||
}
|
||||
}
|
||||
object.add_support_layer(layer_id ++, this_layer_id_interface, height_min, zavg);
|
||||
if (num_interfaces && ! this_layer_contacts_only)
|
||||
++ layer_id_interface;
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
generate_support_layers(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths";
|
||||
|
||||
|
@ -3911,6 +3847,82 @@ void modulate_extrusion_by_overlapping_layers(
|
|||
extrusion_entities_append_paths(extrusions_in_out, std::move(it_fragment->polylines), extrusion_role, it_fragment->mm3_per_mm, it_fragment->width, it_fragment->height);
|
||||
}
|
||||
|
||||
void generate_support_layers(
|
||||
PrintObject &object,
|
||||
const SupportGeneratorLayersPtr &raft_layers,
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &intermediate_layers,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers)
|
||||
{
|
||||
// Install support layers into the object.
|
||||
// A support layer installed on a PrintObject has a unique print_z.
|
||||
SupportGeneratorLayersPtr layers_sorted;
|
||||
layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size() + base_interface_layers.size());
|
||||
layers_append(layers_sorted, raft_layers);
|
||||
layers_append(layers_sorted, bottom_contacts);
|
||||
layers_append(layers_sorted, top_contacts);
|
||||
layers_append(layers_sorted, intermediate_layers);
|
||||
layers_append(layers_sorted, interface_layers);
|
||||
layers_append(layers_sorted, base_interface_layers);
|
||||
// Sort the layers lexicographically by a raising print_z and a decreasing height.
|
||||
std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; });
|
||||
int layer_id = 0;
|
||||
int layer_id_interface = 0;
|
||||
assert(object.support_layers().empty());
|
||||
for (size_t i = 0; i < layers_sorted.size();) {
|
||||
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
|
||||
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
|
||||
size_t j = i + 1;
|
||||
coordf_t zmax = layers_sorted[i]->print_z + EPSILON;
|
||||
for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ;
|
||||
// Assign an average print_z to the set of layers with nearly equal print_z.
|
||||
coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z);
|
||||
coordf_t height_min = layers_sorted[i]->height;
|
||||
bool empty = true;
|
||||
// For snug supports, layers where the direction of the support interface shall change are accounted for.
|
||||
size_t num_interfaces = 0;
|
||||
size_t num_top_contacts = 0;
|
||||
double top_contact_bottom_z = 0;
|
||||
for (size_t u = i; u < j; ++u) {
|
||||
SupportGeneratorLayer &layer = *layers_sorted[u];
|
||||
if (! layer.polygons.empty()) {
|
||||
empty = false;
|
||||
num_interfaces += one_of(layer.layer_type, support_types_interface);
|
||||
if (layer.layer_type == SupporLayerType::TopContact) {
|
||||
++ num_top_contacts;
|
||||
assert(num_top_contacts <= 1);
|
||||
// All top contact layers sharing this print_z shall also share bottom_z.
|
||||
//assert(num_top_contacts == 1 || (top_contact_bottom_z - layer.bottom_z) < EPSILON);
|
||||
top_contact_bottom_z = layer.bottom_z;
|
||||
}
|
||||
}
|
||||
layer.print_z = zavg;
|
||||
height_min = std::min(height_min, layer.height);
|
||||
}
|
||||
if (! empty) {
|
||||
// Here the upper_layer and lower_layer pointers are left to null at the support layers,
|
||||
// as they are never used. These pointers are candidates for removal.
|
||||
bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces;
|
||||
size_t this_layer_id_interface = layer_id_interface;
|
||||
if (this_layer_contacts_only) {
|
||||
// Find a supporting layer for its interface ID.
|
||||
for (auto it = object.support_layers().rbegin(); it != object.support_layers().rend(); ++ it)
|
||||
if (const SupportLayer &other_layer = **it; std::abs(other_layer.print_z - top_contact_bottom_z) < EPSILON) {
|
||||
// other_layer supports this top contact layer. Assign a different support interface direction to this layer
|
||||
// from the layer that supports it.
|
||||
this_layer_id_interface = other_layer.interface_id() + 1;
|
||||
}
|
||||
}
|
||||
object.add_support_layer(layer_id ++, this_layer_id_interface, height_min, zavg);
|
||||
if (num_interfaces && ! this_layer_contacts_only)
|
||||
++ layer_id_interface;
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_support_toolpaths(
|
||||
SupportLayerPtrs &support_layers,
|
||||
const PrintObjectConfig &config,
|
||||
|
|
|
@ -147,6 +147,15 @@ struct SupportParameters {
|
|||
bool with_sheath;
|
||||
};
|
||||
|
||||
void generate_support_layers(
|
||||
PrintObject &object,
|
||||
const SupportGeneratorLayersPtr &raft_layers,
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &intermediate_layers,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers);
|
||||
|
||||
// Produce the support G-code.
|
||||
// Used by both classic and tree supports.
|
||||
void generate_support_toolpaths(
|
||||
|
|
|
@ -80,12 +80,17 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr
|
|||
|
||||
static Polygons calculateMachineBorderCollision(Polygon machine_border)
|
||||
{
|
||||
Polygons machine_volume_border;
|
||||
// Put a border of 1m around the print volume so that we don't collide.
|
||||
append(machine_volume_border, offset(machine_border, scaled<float>(1000.)));
|
||||
#if 1
|
||||
//FIXME just returning no border will let tree support legs collide with print bed boundary
|
||||
return {};
|
||||
#else
|
||||
//FIXME offsetting by 1000mm easily overflows int32_tr coordinate.
|
||||
Polygons out = offset(machine_border, scaled<float>(1000.), jtMiter, 1.2);
|
||||
machine_border.reverse(); // Makes the polygon negative so that we subtract the actual volume from the collision area.
|
||||
machine_volume_border.emplace_back(std::move(machine_border));
|
||||
return machine_volume_border;
|
||||
out.emplace_back(std::move(machine_border));
|
||||
return out;
|
||||
#endif
|
||||
}
|
||||
|
||||
TreeModelVolumes::TreeModelVolumes(
|
||||
|
@ -545,7 +550,6 @@ void TreeModelVolumes::calculateCollision(std::deque<RadiusLayerPair> keys)
|
|||
const coord_t z_distance_bottom = m_layer_outlines[outline_idx].first.support_bottom_distance;
|
||||
const size_t z_distance_bottom_layers = round_up_divide(z_distance_bottom, layer_height);
|
||||
const coord_t z_distance_top_layers = round_up_divide(m_layer_outlines[outline_idx].first.support_top_distance, layer_height);
|
||||
const LayerIndex max_anti_overhang_layer = m_anti_overhang.size() - 1;
|
||||
const LayerIndex max_required_layer = keys[i].second + std::max(coord_t(1), z_distance_top_layers);
|
||||
const coord_t xy_distance = outline_idx == m_current_outline_idx ? m_current_min_xy_dist : m_layer_outlines[outline_idx].first.support_xy_distance;
|
||||
// technically this causes collision for the normal xy_distance to be larger by m_current_min_xy_dist_delta for all not currently processing meshes as this delta will be added at request time.
|
||||
|
@ -566,49 +570,48 @@ void TreeModelVolumes::calculateCollision(std::deque<RadiusLayerPair> keys)
|
|||
if (size_t(layer_idx) < m_layer_outlines[outline_idx].second.size())
|
||||
append(collision_areas, m_layer_outlines[outline_idx].second[layer_idx]);
|
||||
// jtRound is not needed here, as the overshoot can not cause errors in the algorithm, because no assumptions are made about the model.
|
||||
collision_areas = offset(union_ex(collision_areas), radius + xy_distance, ClipperLib::jtMiter, 1.2);
|
||||
append(data[key], collision_areas); // if a key does not exist when it is accessed it is added!
|
||||
// if a key does not exist when it is accessed it is added!
|
||||
append(data[key], offset(union_ex(collision_areas), radius + xy_distance, ClipperLib::jtMiter, 1.2));
|
||||
}
|
||||
|
||||
// Add layers below, to ensure correct support_bottom_distance. Also save placeable areas of radius 0, if required for this mesh.
|
||||
for (LayerIndex layer_idx = max_required_layer; layer_idx >= min_layer_bottom; -- layer_idx) {
|
||||
for (int layer_idx = int(max_required_layer); layer_idx >= min_layer_bottom; -- layer_idx) {
|
||||
key.second = layer_idx;
|
||||
for (size_t layer_offset = 1; layer_offset <= z_distance_bottom_layers && layer_idx - coord_t(layer_offset) > min_layer_bottom; ++ layer_offset)
|
||||
append(data[key], data[RadiusLayerPair(radius, layer_idx - layer_offset)]);
|
||||
if (support_rests_on_this_model && radius == 0 && layer_idx < coord_t(1 + keys[i].second)) {
|
||||
data[key] = union_(data[key]);
|
||||
Polygons above = data[RadiusLayerPair(radius, layer_idx + 1)];
|
||||
RadiusLayerPair key_next_layer(radius, layer_idx + 1);
|
||||
//data[key] = union_(data[key]);
|
||||
Polygons above = data[key_next_layer];
|
||||
// just to be sure the area is correctly unioned as otherwise difference may behave unexpectedly.
|
||||
above = max_anti_overhang_layer >= layer_idx + 1 ? union_(above, m_anti_overhang[layer_idx]) : union_(above);
|
||||
Polygons placeable = diff(data[key], above);
|
||||
data_placeable[RadiusLayerPair(radius, layer_idx + 1)] = union_(data_placeable[RadiusLayerPair(radius, layer_idx + 1)], placeable);
|
||||
//FIXME Vojtech: Why m_anti_overhang.size() > layer_idx + 1? Why +1?
|
||||
above = m_anti_overhang.size() > layer_idx + 1 ? union_(above, m_anti_overhang[layer_idx]) : union_(above);
|
||||
data_placeable[key_next_layer] = union_(data_placeable[key_next_layer], diff(data[key], above));
|
||||
}
|
||||
}
|
||||
|
||||
// Add collision layers above to ensure correct support_top_distance.
|
||||
for (LayerIndex layer_idx = min_layer_bottom; layer_idx <= max_required_layer; layer_idx++) {
|
||||
for (LayerIndex layer_idx = min_layer_bottom; layer_idx <= max_required_layer; ++ layer_idx) {
|
||||
key.second = layer_idx;
|
||||
for (coord_t layer_offset = 1; layer_offset <= z_distance_top_layers && layer_offset + layer_idx <= max_required_layer; layer_offset++)
|
||||
append(data[key], data[RadiusLayerPair(radius, layer_idx + layer_offset)]);
|
||||
data[key] = max_anti_overhang_layer >= layer_idx ? union_(data[key], offset(union_ex(m_anti_overhang[layer_idx]), radius, ClipperLib::jtMiter, 1.2)) : union_(data[key]);
|
||||
Polygons collisions = std::move(data[key]);
|
||||
for (coord_t layer_offset = 1; layer_offset <= z_distance_top_layers && layer_offset + layer_idx <= max_required_layer; ++ layer_offset)
|
||||
append(collisions, data[RadiusLayerPair(radius, layer_idx + layer_offset)]);
|
||||
data[key] = m_anti_overhang.size() > layer_idx ? union_(collisions, offset(union_ex(m_anti_overhang[layer_idx]), radius, ClipperLib::jtMiter, 1.2)) : union_(collisions);
|
||||
}
|
||||
|
||||
for (LayerIndex layer_idx = max_required_layer; layer_idx > keys[i].second; layer_idx--) {
|
||||
for (int layer_idx = int(max_required_layer); layer_idx > keys[i].second; -- layer_idx) {
|
||||
// all these dont have the correct z_distance_top_layers as they can still have areas above them
|
||||
auto it = data.find(RadiusLayerPair(radius, layer_idx));
|
||||
if (it != data.end())
|
||||
data.erase(it);
|
||||
}
|
||||
|
||||
for (auto pair : data) {
|
||||
pair.second = simplify(pair.second, m_min_resolution);
|
||||
data_outer[pair.first] = union_(data_outer[pair.first], pair.second);
|
||||
for (auto pair : data)
|
||||
data_outer[pair.first] = union_(data_outer[pair.first], simplify(pair.second, m_min_resolution));
|
||||
if (radius == 0) {
|
||||
for (auto pair : data_placeable)
|
||||
data_placeable_outer[pair.first] = union_(data_placeable_outer[pair.first], simplify(pair.second, m_min_resolution));
|
||||
}
|
||||
if (radius == 0)
|
||||
for (auto pair : data_placeable) {
|
||||
pair.second = simplify(pair.second, m_min_resolution);
|
||||
data_placeable_outer[pair.first] = union_(data_placeable_outer[pair.first], pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
||||
|
@ -648,7 +651,7 @@ void TreeModelVolumes::calculateCollisionHolefree(std::deque<RadiusLayerPair> ke
|
|||
coord_t radius = key.first;
|
||||
coord_t increase_radius_ceil = ceilRadius(m_increase_until_radius, false) - ceilRadius(radius, true);
|
||||
// this union is important as otherwise holes(in form of lines that will increase to holes in a later step) can get unioned onto the area.
|
||||
Polygons col = offset(union_ex(getCollision(m_increase_until_radius, layer_idx, false)), 5 - increase_radius_ceil, ClipperLib::jtRound);
|
||||
Polygons col = offset(union_ex(getCollision(m_increase_until_radius, layer_idx, false)), 5 - increase_radius_ceil, ClipperLib::jtRound, scaled<float>(0.01));
|
||||
col = simplify(col, m_min_resolution);
|
||||
data[RadiusLayerPair(radius, layer_idx)] = col;
|
||||
}
|
||||
|
@ -663,11 +666,11 @@ void TreeModelVolumes::calculateCollisionHolefree(std::deque<RadiusLayerPair> ke
|
|||
static Polygons safeOffset(const Polygons& me, coord_t distance, ClipperLib::JoinType jt, coord_t max_safe_step_distance, const Polygons& collision)
|
||||
{
|
||||
const size_t steps = std::abs(distance / max_safe_step_distance);
|
||||
assert(distance * max_safe_step_distance >= 0);
|
||||
assert(int64_t(distance) * int64_t(max_safe_step_distance) >= 0);
|
||||
ExPolygons ret = union_ex(me);
|
||||
for (size_t i = 0; i < steps; ++ i)
|
||||
ret = union_ex(union_(offset(ret, max_safe_step_distance, jt, 1.2), collision));
|
||||
return union_(offset(ret, distance % max_safe_step_distance, jt, 1.2), collision);
|
||||
ret = union_ex(union_(offset(ret, max_safe_step_distance, jt, jt == jtRound ? scaled<float>(0.01) : 1.2), collision));
|
||||
return union_(offset(ret, distance % max_safe_step_distance, jt, jt == jtRound ? scaled<float>(0.01) : 1.2), collision);
|
||||
}
|
||||
|
||||
void TreeModelVolumes::calculateAvoidance(std::deque<RadiusLayerPair> keys)
|
||||
|
@ -768,7 +771,7 @@ void TreeModelVolumes::calculatePlaceables(std::deque<RadiusLayerPair> keys)
|
|||
key.second = layer;
|
||||
Polygons placeable = getPlaceableAreas(0, layer);
|
||||
placeable = simplify(placeable, m_min_resolution); // it is faster to do this here in each thread than once in calculateCollision.
|
||||
placeable = offset(union_ex(placeable), - radius);
|
||||
placeable = offset(union_ex(placeable), - radius, jtMiter, 1.2);
|
||||
data[layer] = std::pair<RadiusLayerPair, Polygons>(key, placeable);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <tbb/global_control.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
@ -124,7 +126,7 @@ void TreeSupport::showError(std::string message, bool critical)
|
|||
static bool layer_has_overhangs(const Layer &layer)
|
||||
{
|
||||
for (const LayerRegion* layerm : layer.regions())
|
||||
if (layerm->slices.has(stBottom) || layerm->slices.has(stBottom))
|
||||
if (layerm->slices.has(stBottom) || layerm->slices.has(stBottomBridge))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -172,7 +174,7 @@ void TreeSupport::generateSupportAreas(PrintObject& print_object)
|
|||
break;
|
||||
++ idx;
|
||||
}
|
||||
this->generateSupportAreas(*print_object.print(), BuildVolume(Pointfs{ Vec2d{ -1000., -1000. }, Vec2d{ -1000., +1000. }, Vec2d{ +1000., +1000. }, Vec2d{ +1000., -1000. } }, 0.), { idx });
|
||||
this->generateSupportAreas(*print_object.print(), BuildVolume(Pointfs{ Vec2d{ -300., -300. }, Vec2d{ -300., +300. }, Vec2d{ +300., +300. }, Vec2d{ +300., -300. } }, 0.), { idx });
|
||||
}
|
||||
|
||||
void TreeSupport::generateSupportAreas(Print &print, const BuildVolume &build_volume, const std::vector<size_t> &print_object_ids)
|
||||
|
@ -267,9 +269,17 @@ void TreeSupport::generateSupportAreas(Print &print, const BuildVolume &build_vo
|
|||
}
|
||||
}
|
||||
|
||||
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.
|
||||
// Used by both classic and tree supports.
|
||||
SupportGeneratorLayersPtr raft_layers, interface_layers, base_interface_layers;
|
||||
generate_support_layers(print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
|
||||
generate_support_toolpaths(print_object.support_layers(), print_object.config(), SupportParameters(print_object), print_object.slicing_parameters(),
|
||||
raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
|
||||
|
||||
|
@ -527,7 +537,7 @@ static [[nodiscard]] Polylines ensureMaximumDistancePolyline(const Polylines &in
|
|||
{
|
||||
Polylines result;
|
||||
for (Polyline part : input) {
|
||||
if (part.empty() == 0)
|
||||
if (part.empty())
|
||||
continue;
|
||||
|
||||
double len = length(part.points);
|
||||
|
@ -537,7 +547,7 @@ static [[nodiscard]] Polylines ensureMaximumDistancePolyline(const Polylines &in
|
|||
{
|
||||
// Insert the opposite point of the first one.
|
||||
//FIXME pretty expensive
|
||||
Polyline pl(line.points);
|
||||
Polyline pl(part);
|
||||
pl.clip_end(len / 2);
|
||||
line.points.emplace_back(pl.points.back());
|
||||
}
|
||||
|
@ -673,7 +683,7 @@ static [[nodiscard]] Polylines generateSupportInfillLines(
|
|||
|
||||
filler->layer_id = layer_idx;
|
||||
filler->spacing = flow.spacing();
|
||||
fill_params.density = float(roof ? support_params.interface_density : float(filler->spacing) / float(support_infill_distance));
|
||||
fill_params.density = float(roof ? support_params.interface_density : scaled<float>(filler->spacing) / float(support_infill_distance));
|
||||
fill_params.dont_adjust = true;
|
||||
|
||||
Polylines out;
|
||||
|
@ -722,7 +732,7 @@ static [[nodiscard]] Polygons safeUnion(const Polygons first, const Polygons sec
|
|||
if (result.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Caught an area destroying union, enlarging areas a bit.";
|
||||
// just take the few lines we have, and offset them a tiny bit. Needs to be offsetPolylines, as offset may aleady have problems with the area.
|
||||
result = union_(offset(to_polylines(first), scaled<float>(0.002)), offset(to_polylines(second), scaled<float>(0.002)));
|
||||
result = union_(offset(to_polylines(first), scaled<float>(0.002), jtMiter, 1.2), offset(to_polylines(second), scaled<float>(0.002), jtMiter, 1.2));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -772,13 +782,16 @@ static [[nodiscard]] Polygons safeOffsetInc(const Polygons& me, coord_t distance
|
|||
}
|
||||
// offset in steps
|
||||
for (size_t i = 0; i < steps; i++) {
|
||||
ret = diff(offset(ret, step_size, ClipperLib::jtRound), collision);
|
||||
ret = diff(offset(ret, step_size, ClipperLib::jtRound, scaled<float>(0.01)), collision);
|
||||
// ensure that if many offsets are done the performance does not suffer extremely by the new vertices of jtRound.
|
||||
if (i % 10 == 7)
|
||||
ret = polygons_simplify(ret, scaled<double>(0.015));
|
||||
}
|
||||
// offset the remainder
|
||||
ret = polygons_simplify(offset(ret, distance - steps * step_size, ClipperLib::jtRound), scaled<double>(0.015));
|
||||
float last_offset = distance - steps * step_size;
|
||||
if (last_offset > SCALED_EPSILON)
|
||||
ret = offset(ret, distance - steps * step_size, ClipperLib::jtRound, scaled<float>(0.01));
|
||||
ret = polygons_simplify(ret, scaled<double>(0.015));
|
||||
|
||||
if (do_final_difference)
|
||||
ret = diff(ret, collision);
|
||||
|
@ -787,12 +800,36 @@ static [[nodiscard]] Polygons safeOffsetInc(const Polygons& me, coord_t distance
|
|||
|
||||
// Using the std::deque as an allocator.
|
||||
inline SupportGeneratorLayer& layer_allocate(
|
||||
std::deque<SupportGeneratorLayer>& layer_storage,
|
||||
SupporLayerType layer_type)
|
||||
std::deque<SupportGeneratorLayer> &layer_storage,
|
||||
SupporLayerType layer_type,
|
||||
const SlicingParameters &slicing_params,
|
||||
size_t layer_idx)
|
||||
{
|
||||
layer_storage.push_back(SupportGeneratorLayer());
|
||||
layer_storage.back().layer_type = layer_type;
|
||||
return layer_storage.back();
|
||||
SupportGeneratorLayer *layer_new = &layer_storage.back();
|
||||
layer_new->layer_type = layer_type;
|
||||
layer_new->print_z = slicing_params.first_print_layer_height + std::max(0, int(layer_idx) - 1) * slicing_params.layer_height;
|
||||
layer_new->height = slicing_params.layer_height;
|
||||
layer_new->bottom_z = layer_idx == 0 ? 0. : layer_new->print_z - slicing_params.layer_height;
|
||||
return *layer_new;
|
||||
}
|
||||
|
||||
inline SupportGeneratorLayer& layer_allocate(
|
||||
std::deque<SupportGeneratorLayer> &layer_storage,
|
||||
tbb::spin_mutex& layer_storage_mutex,
|
||||
SupporLayerType layer_type,
|
||||
const SlicingParameters &slicing_params,
|
||||
size_t layer_idx)
|
||||
{
|
||||
layer_storage_mutex.lock();
|
||||
layer_storage.push_back(SupportGeneratorLayer());
|
||||
SupportGeneratorLayer *layer_new = &layer_storage.back();
|
||||
layer_storage_mutex.unlock();
|
||||
layer_new->layer_type = layer_type;
|
||||
layer_new->print_z = slicing_params.first_print_layer_height + std::max(0, int(layer_idx) - 1) * slicing_params.layer_height;
|
||||
layer_new->height = slicing_params.layer_height;
|
||||
layer_new->bottom_z = layer_idx == 0 ? 0. : layer_new->print_z - slicing_params.layer_height;
|
||||
return *layer_new;
|
||||
}
|
||||
|
||||
void TreeSupport::generateInitialAreas(
|
||||
|
@ -802,6 +839,8 @@ void TreeSupport::generateInitialAreas(
|
|||
SupportGeneratorLayersPtr &top_interface_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage)
|
||||
{
|
||||
tbb::global_control(tbb::global_control::max_allowed_parallelism, 1);
|
||||
|
||||
Polygon base_circle;
|
||||
const int base_radius = 10;
|
||||
for (unsigned int i = 0; i < SUPPORT_TREE_CIRCLE_RESOLUTION; ++ i) {
|
||||
|
@ -841,10 +880,13 @@ void TreeSupport::generateInitialAreas(
|
|||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
if (! layer_has_overhangs(*print_object.get_layer(layer_idx + z_distance_delta)))
|
||||
continue;
|
||||
|
||||
Polygons relevant_forbidden = (mesh_config.support_rests_on_model ? (SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::FAST, true, !xy_overrides_z) : m_volumes.getCollision(mesh_config.getRadius(0), layer_idx, !xy_overrides_z)) : m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::FAST, false, !xy_overrides_z)); // take the least restrictive avoidance possible
|
||||
// take the least restrictive avoidance possible
|
||||
Polygons relevant_forbidden = (mesh_config.support_rests_on_model ?
|
||||
(SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::FAST, true, !xy_overrides_z) :
|
||||
m_volumes.getCollision(mesh_config.getRadius(0), layer_idx, !xy_overrides_z)) :
|
||||
m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx, AvoidanceType::FAST, false, !xy_overrides_z));
|
||||
// prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise.
|
||||
relevant_forbidden = offset(union_ex(relevant_forbidden), scaled<float>(0.005));
|
||||
relevant_forbidden = offset(union_ex(relevant_forbidden), scaled<float>(0.005), jtMiter, 1.2);
|
||||
|
||||
auto generateLines = [&](const Polygons& area, bool roof, LayerIndex layer_idx) -> Polylines {
|
||||
const coord_t support_infill_distance = roof ? mesh_group_settings.support_roof_line_distance : mesh_group_settings.support_tree_branch_distance;
|
||||
|
@ -921,13 +963,15 @@ void TreeSupport::generateInitialAreas(
|
|||
roof_circle.points.emplace_back(p.first + corner * mesh_config.min_radius / base_radius);
|
||||
added_roofs.emplace_back(roof_circle);
|
||||
}
|
||||
added_roofs = union_(added_roofs);
|
||||
{
|
||||
std::lock_guard<std::mutex> critical_section_storage(critical_sections);
|
||||
SupportGeneratorLayer *&l = top_contacts[insert_layer_idx - dtt_roof_tip];
|
||||
if (l == nullptr)
|
||||
l = &layer_allocate(layer_storage, SupporLayerType::TopContact);
|
||||
append(l->polygons, std::move(added_roofs));
|
||||
if (! added_roofs.empty()) {
|
||||
added_roofs = union_(added_roofs);
|
||||
{
|
||||
std::lock_guard<std::mutex> critical_section_storage(critical_sections);
|
||||
SupportGeneratorLayer *&l = top_contacts[insert_layer_idx - dtt_roof_tip];
|
||||
if (l == nullptr)
|
||||
l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), insert_layer_idx - dtt_roof_tip);
|
||||
append(l->polygons, std::move(added_roofs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -945,7 +989,7 @@ void TreeSupport::generateInitialAreas(
|
|||
Polygons overhang_raw = layer_overhangs(*print_object.get_layer(layer_idx + z_distance_delta));
|
||||
Polygons overhang_regular = safeOffsetInc(overhang_raw, mesh_group_settings.support_offset, relevant_forbidden, mesh_config.min_radius * 1.75 + mesh_config.xy_min_distance, 0, 1);
|
||||
// offset ensures that areas that could be supported by a part of a support line, are not considered unsupported overhang
|
||||
Polygons remaining_overhang = intersection(diff(offset(union_ex(overhang_raw), mesh_group_settings.support_offset), offset(union_ex(overhang_regular), mesh_config.support_line_width * 0.5)), relevant_forbidden);
|
||||
Polygons remaining_overhang = intersection(diff(offset(union_ex(overhang_raw), mesh_group_settings.support_offset, jtMiter, 1.2), offset(union_ex(overhang_regular), mesh_config.support_line_width * 0.5, jtMiter, 1.2)), relevant_forbidden);
|
||||
coord_t extra_total_offset_acc = 0;
|
||||
|
||||
// Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible.
|
||||
|
@ -1032,7 +1076,7 @@ void TreeSupport::generateInitialAreas(
|
|||
// here the roof is handled. If roof can not be added the branches will try to not move instead
|
||||
Polygons forbidden_next = (mesh_config.support_rests_on_model ? (SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL ? m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), AvoidanceType::FAST, true, !xy_overrides_z) : m_volumes.getCollision(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), !xy_overrides_z)) : m_volumes.getAvoidance(mesh_config.getRadius(0), layer_idx - (dtt_roof + 1), AvoidanceType::FAST, false, !xy_overrides_z));\
|
||||
// prevent rounding errors down the line
|
||||
forbidden_next = offset(union_ex(forbidden_next), scaled<float>(0.005));
|
||||
forbidden_next = offset(union_ex(forbidden_next), scaled<float>(0.005), jtMiter, 1.2);
|
||||
Polygons overhang_outset_next = diff(overhang_outset, forbidden_next);
|
||||
if (area(overhang_outset_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
|
||||
|
@ -1066,13 +1110,14 @@ void TreeSupport::generateInitialAreas(
|
|||
|
||||
{
|
||||
std::lock_guard<std::mutex> critical_section_storage(critical_sections);
|
||||
for (size_t idx = 0; idx < dtt_roof; idx++) {
|
||||
SupportGeneratorLayer *&l = top_contacts[layer_idx - idx];
|
||||
if (l == nullptr)
|
||||
l = &layer_allocate(layer_storage, SupporLayerType::TopContact);
|
||||
// will be unioned in finalizeInterfaceAndSupportAreas
|
||||
append(l->polygons, std::move(added_roofs[idx]));
|
||||
}
|
||||
for (size_t idx = 0; idx < dtt_roof; ++ idx)
|
||||
if (! added_roofs[idx].empty()) {
|
||||
SupportGeneratorLayer *&l = top_contacts[layer_idx - idx];
|
||||
if (l == nullptr)
|
||||
l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), layer_idx - idx);
|
||||
// will be unioned in finalizeInterfaceAndSupportAreas
|
||||
append(l->polygons, std::move(added_roofs[idx]));
|
||||
}
|
||||
}
|
||||
|
||||
if (overhang_lines.empty()) {
|
||||
|
@ -1088,9 +1133,9 @@ void TreeSupport::generateInitialAreas(
|
|||
// I assume that even small overhangs are over one line width wide, so lets try to place the support points in a way that the full support area generated from them
|
||||
// will support the overhang (if this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60<36> so there is a fallback,
|
||||
// as some support is better than none.
|
||||
Polygons reduced_overhang_outset = offset(union_ex(overhang_outset), -mesh_config.support_line_width / 2.2);
|
||||
Polygons reduced_overhang_outset = offset(union_ex(overhang_outset), -mesh_config.support_line_width / 2.2, jtMiter, 1.2);
|
||||
polylines = ensureMaximumDistancePolyline(
|
||||
to_polylines(!reduced_overhang_outset.empty() && area(offset(diff_ex(overhang_outset, reduced_overhang_outset), std::max(mesh_config.support_line_width, connect_length))) < 1 ?
|
||||
to_polylines(!reduced_overhang_outset.empty() && area(offset(diff_ex(overhang_outset, reduced_overhang_outset), std::max(mesh_config.support_line_width, connect_length), jtMiter, 1.2)) < 1 ?
|
||||
reduced_overhang_outset :
|
||||
overhang_outset),
|
||||
connect_length, min_support_points);
|
||||
|
@ -1099,11 +1144,11 @@ void TreeSupport::generateInitialAreas(
|
|||
overhang_lines = convertLinesToInternal(m_volumes, m_config, polylines, last_insert_layer);
|
||||
}
|
||||
|
||||
if (int(dtt_roof) >= layer_idx && roof_allowed_for_this_part) { // reached buildplate
|
||||
if (int(dtt_roof) >= layer_idx && roof_allowed_for_this_part && ! overhang_outset.empty()) { // reached buildplate
|
||||
std::lock_guard<std::mutex> critical_section_storage(critical_sections);
|
||||
SupportGeneratorLayer*& l = top_contacts[0];
|
||||
if (l == nullptr)
|
||||
l = &layer_allocate(layer_storage, SupporLayerType::TopContact);
|
||||
l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), 0);
|
||||
append(l->polygons, std::move(overhang_outset));
|
||||
} else // normal trees have to be generated
|
||||
addLinesAsInfluenceAreas(overhang_lines, force_tip_to_roof ? support_roof_layers - dtt_roof : 0, layer_idx - dtt_roof, dtt_roof > 0, roof_enabled ? support_roof_layers - dtt_roof : 0);
|
||||
|
@ -1314,7 +1359,7 @@ static void mergeHelper(
|
|||
Polygons intersect = intersection(small_rad_increased_by_big_minus_small, bigger_rad.second);
|
||||
|
||||
if (area(intersect) > tiny_area_threshold) { // dont use empty as a line is not empty, but for this use-case it very well may be (and would be one layer down as union does not keep lines)
|
||||
if (area(offset(intersect, scaled<float>(-0.025))) <= tiny_area_threshold) // check if the overlap is large enough (Small ares tend to attract rounding errors in clipper). While 25 was guessed as enough, i did not have reason to change it.
|
||||
if (area(offset(intersect, scaled<float>(-0.025), jtMiter, 1.2)) <= tiny_area_threshold) // check if the overlap is large enough (Small ares tend to attract rounding errors in clipper). While 25 was guessed as enough, i did not have reason to change it.
|
||||
continue;
|
||||
|
||||
// Do the actual merge now that the branches are confirmed to be able to intersect.
|
||||
|
@ -1365,7 +1410,7 @@ static void mergeHelper(
|
|||
|
||||
erase.emplace_back(reduced_check_iter->first);
|
||||
erase.emplace_back(influence_iter->first);
|
||||
Polygons merge = diff(offset(union_(intersect, intersect_sec), config.getRadius(key), ClipperLib::jtRound), volumes.getCollision(0, layer_idx - 1)); // regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer.
|
||||
Polygons merge = diff(offset(union_(intersect, intersect_sec), config.getRadius(key), ClipperLib::jtRound, scaled<float>(0.01)), volumes.getCollision(0, layer_idx - 1)); // regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer.
|
||||
|
||||
reduced_aabb.erase(reduced_check_iter->first); // this invalidates reduced_check_iter
|
||||
reduced_aabb.emplace(key, get_extents(merge));
|
||||
|
@ -1569,8 +1614,11 @@ std::optional<TreeSupport::SupportElement> TreeSupport::increaseSingleArea(AreaI
|
|||
radius = m_config.getCollisionRadius(current_elem);
|
||||
|
||||
const coord_t foot_radius_increase = m_config.branch_radius * (std::max(m_config.diameter_scale_bp_radius - m_config.diameter_angle_scale_factor, 0.0));
|
||||
double planned_foot_increase = std::min(1.0, double(m_config.recommendedMinRadius(layer_idx - 1) - m_config.getRadius(current_elem)) / foot_radius_increase); // Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, which could cause the radius to become bigger than precalculated.
|
||||
// Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, which could cause the radius to become bigger than precalculated.
|
||||
double planned_foot_increase = std::min(1.0, double(m_config.recommendedMinRadius(layer_idx - 1) - m_config.getRadius(current_elem)) / foot_radius_increase);
|
||||
//FIXME
|
||||
bool increase_bp_foot = planned_foot_increase > 0 && current_elem.to_buildplate;
|
||||
// bool increase_bp_foot = false;
|
||||
|
||||
if (increase_bp_foot && m_config.getRadius(current_elem) >= m_config.branch_radius && m_config.getRadius(current_elem) >= m_config.increase_radius_until_radius)
|
||||
if (validWithRadius(m_config.getRadius(current_elem.effective_radius_height, current_elem.elephant_foot_increases + planned_foot_increase))) {
|
||||
|
@ -1749,7 +1797,7 @@ void TreeSupport::increaseAreas(std::unordered_map<SupportElement, Polygons>& to
|
|||
if (!settings.no_error) {
|
||||
// ERROR CASE
|
||||
// if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, it still actually has an area that can be increased
|
||||
Polygons lines_offset = offset(to_polylines(*parent->area), scaled<float>(0.005));
|
||||
Polygons lines_offset = offset(to_polylines(*parent->area), scaled<float>(0.005), jtMiter, 1.2);
|
||||
Polygons base_error_area = union_(*parent->area, lines_offset);
|
||||
result = increaseSingleArea(settings, layer_idx, parent, base_error_area, to_bp_data, to_model_data, inc_wo_collision, (m_config.maximum_move_distance + extra_speed) * 1.5, mergelayer);
|
||||
BOOST_LOG_TRIVIAL(error) <<
|
||||
|
@ -1832,7 +1880,7 @@ void TreeSupport::createLayerPathing(std::vector<std::set<SupportElement*>>& mov
|
|||
size_t merge_every_x_layers = 1;
|
||||
// Calculate the influence areas for each layer below (Top down)
|
||||
// This is done by first increasing the influence area by the allowed movement distance, and merging them with other influence areas if possible
|
||||
for (LayerIndex layer_idx = move_bounds.size() - 1; layer_idx > 0; layer_idx--)
|
||||
for (int layer_idx = int(move_bounds.size()) - 1; layer_idx > 0; -- layer_idx)
|
||||
{
|
||||
// merging is expensive and only parallelized to a max speedup of 2. As such it may be useful in some cases to only merge every few layers to improve performance.
|
||||
bool merge_this_layer = size_t(last_merge - layer_idx) >= merge_every_x_layers;
|
||||
|
@ -2138,7 +2186,8 @@ void TreeSupport::generateBranchAreas(std::vector<std::pair<LayerIndex, SupportE
|
|||
poly.emplace_back(std::move(circle));
|
||||
}
|
||||
|
||||
poly = diff(offset(union_(poly), std::min(coord_t(50), m_config.support_line_width / 4)), m_volumes.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)); // There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be. This can cause the tip to be slightly further away front the overhang (x/y wise) than optimal. This fixes it, and for every other part, 0.05mm will not be noticed.
|
||||
poly = diff(offset(union_(poly), std::min(coord_t(50), m_config.support_line_width / 4), jtMiter, 1.2),
|
||||
m_volumes.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)); // There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be. This can cause the tip to be slightly further away front the overhang (x/y wise) than optimal. This fixes it, and for every other part, 0.05mm will not be noticed.
|
||||
return poly;
|
||||
};
|
||||
|
||||
|
@ -2171,7 +2220,7 @@ void TreeSupport::generateBranchAreas(std::vector<std::pair<LayerIndex, SupportE
|
|||
}
|
||||
}
|
||||
// Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above.
|
||||
linear_inserts[idx] = offset(polygons_with_correct_center, m_config.support_line_width / 2);
|
||||
linear_inserts[idx] = offset(polygons_with_correct_center, m_config.support_line_width / 2, jtMiter, 1.2);
|
||||
linear_inserts[idx] = diff(linear_inserts[idx], m_volumes.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist));
|
||||
}
|
||||
}
|
||||
|
@ -2201,7 +2250,7 @@ void TreeSupport::smoothBranchAreas(std::vector<std::unordered_map<SupportElemen
|
|||
const coord_t max_radius_change_per_layer = 1 + m_config.support_line_width / 2; // this is the upper limit a radius may change per layer. +1 to avoid rounding errors
|
||||
|
||||
// smooth upwards
|
||||
for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(layer_tree_polygons.size()) - 1; layer_idx++) {
|
||||
for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(layer_tree_polygons.size()) - 1; ++ layer_idx) {
|
||||
std::vector<std::pair<SupportElement*, Polygons>> processing;
|
||||
processing.insert(processing.end(), layer_tree_polygons[layer_idx].begin(), layer_tree_polygons[layer_idx].end());
|
||||
std::vector<std::vector<std::pair<SupportElement*, Polygons>>> update_next(processing.size()); // with this a lock can be avoided
|
||||
|
@ -2219,7 +2268,7 @@ void TreeSupport::smoothBranchAreas(std::vector<std::unordered_map<SupportElemen
|
|||
}
|
||||
max_outer_wall_distance += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers.
|
||||
if (do_something) {
|
||||
Polygons max_allowed_area = offset(data_pair.second, float(max_outer_wall_distance));
|
||||
Polygons max_allowed_area = offset(data_pair.second, float(max_outer_wall_distance), jtMiter, 1.2);
|
||||
for (SupportElement* parent : data_pair.first->parents)
|
||||
if (m_config.getRadius(*parent) != m_config.getCollisionRadius(*parent))
|
||||
update_next[processing_idx].emplace_back(std::pair<SupportElement*, Polygons>(parent, intersection(layer_tree_polygons[layer_idx + 1][parent], max_allowed_area)));
|
||||
|
@ -2239,7 +2288,7 @@ void TreeSupport::smoothBranchAreas(std::vector<std::unordered_map<SupportElemen
|
|||
|
||||
// smooth downwards
|
||||
std::unordered_set<SupportElement*> updated_last_iteration;
|
||||
for (LayerIndex layer_idx = layer_tree_polygons.size() - 2; layer_idx >= 0; layer_idx--) {
|
||||
for (int layer_idx = int(layer_tree_polygons.size()) - 2; layer_idx >= 0; -- layer_idx) {
|
||||
std::vector<std::pair<SupportElement*, Polygons>> processing;
|
||||
processing.insert(processing.end(), layer_tree_polygons[layer_idx].begin(), layer_tree_polygons[layer_idx].end());
|
||||
std::vector<std::pair<SupportElement*, Polygons>> update_next(processing.size(), std::pair<SupportElement*, Polygons>(nullptr, Polygons())); // with this a lock can be avoided
|
||||
|
@ -2253,7 +2302,7 @@ void TreeSupport::smoothBranchAreas(std::vector<std::unordered_map<SupportElemen
|
|||
for (size_t idx = 0; idx < data_pair.first->parents.size(); ++ idx) {
|
||||
SupportElement* parent = data_pair.first->parents[idx];
|
||||
coord_t max_outer_line_increase = max_radius_change_per_layer;
|
||||
Polygons result = offset(layer_tree_polygons[layer_idx + 1][parent], max_outer_line_increase);
|
||||
Polygons result = offset(layer_tree_polygons[layer_idx + 1][parent], max_outer_line_increase, jtMiter, 1.2);
|
||||
Point direction = data_pair.first->result_on_layer - parent->result_on_layer;
|
||||
// move the polygons object
|
||||
for (auto& outer : result)
|
||||
|
@ -2326,7 +2375,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(
|
|||
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
||||
|
||||
// Iterate over the generated circles in parallel and clean them up. Also add support floor.
|
||||
std::mutex critical_sections;
|
||||
tbb::spin_mutex layer_storage_mutex;
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, support_layer_storage.size()),
|
||||
[&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
|
@ -2338,7 +2387,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(
|
|||
SupportGeneratorLayer*& support_roof = top_contacts[layer_idx];
|
||||
if (! support_roof_storage[layer_idx].empty() || support_roof != nullptr) {
|
||||
if (support_roof == nullptr) {
|
||||
support_roof = &layer_allocate(layer_storage, SupporLayerType::TopContact);
|
||||
support_roof = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::TopContact, print_object.slicing_parameters(), layer_idx);
|
||||
support_roof->polygons = union_(support_roof_storage[layer_idx]);
|
||||
} else
|
||||
support_roof->polygons = union_(support_roof->polygons, support_roof_storage[layer_idx]);
|
||||
|
@ -2383,10 +2432,10 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(
|
|||
// Subtract support floors from the support area and add them to the support floor instead.
|
||||
if (m_config.support_bottom_layers > 0 && !support_layer_storage[layer_idx].empty()) {
|
||||
SupportGeneratorLayer*& support_bottom = bottom_contacts[layer_idx];
|
||||
if (support_bottom == nullptr)
|
||||
support_bottom = &layer_allocate(layer_storage, SupporLayerType::BottomContact);
|
||||
Polygons floor_layer = std::move(support_bottom->polygons);
|
||||
Polygons layer_outset = diff(offset(support_layer_storage[layer_idx], m_config.support_bottom_offset), m_volumes.getCollision(0, layer_idx, false));
|
||||
Polygons layer_outset = diff(
|
||||
m_config.support_bottom_offset > 0 ? offset(support_layer_storage[layer_idx], m_config.support_bottom_offset, jtMiter, 1.2) : support_layer_storage[layer_idx],
|
||||
m_volumes.getCollision(0, layer_idx, false));
|
||||
Polygons floor_layer;
|
||||
size_t layers_below = 0;
|
||||
while (layers_below <= m_config.support_bottom_layers) {
|
||||
// one sample at 0 layers below, another at m_config.support_bottom_layers. In-between samples at m_config.performance_interface_skip_layers distance from each other.
|
||||
|
@ -2398,14 +2447,18 @@ void TreeSupport::finalizeInterfaceAndSupportAreas(
|
|||
else
|
||||
break;
|
||||
}
|
||||
support_bottom->polygons = union_(floor_layer);
|
||||
support_layer_storage[layer_idx] = diff(support_layer_storage[layer_idx], offset(support_bottom->polygons, scaled<float>(0.01))); // Subtract the support floor from the normal support.
|
||||
if (! floor_layer.empty()) {
|
||||
if (support_bottom == nullptr)
|
||||
support_bottom = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::BottomContact, print_object.slicing_parameters(), layer_idx);
|
||||
support_bottom->polygons = union_(floor_layer, support_bottom->polygons);
|
||||
support_layer_storage[layer_idx] = diff(support_layer_storage[layer_idx], offset(support_bottom->polygons, scaled<float>(0.01), jtMiter, 1.2)); // Subtract the support floor from the normal support.
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (! support_layer_storage[layer_idx].empty()) {
|
||||
SupportGeneratorLayer *&l = intermediate_layers[layer_idx];
|
||||
if (l == nullptr)
|
||||
l = &layer_allocate(layer_storage, SupporLayerType::Base);
|
||||
l = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::Base, print_object.slicing_parameters(), layer_idx);
|
||||
append(l->polygons, union_(support_layer_storage[layer_idx]));
|
||||
}
|
||||
|
||||
|
|
|
@ -200,10 +200,11 @@ public:
|
|||
if (config.diameter_scale_bp_radius > 0)
|
||||
{
|
||||
coord_t foot_increase_radius = std::abs(std::max(config.getCollisionRadius(second), config.getCollisionRadius(first)) - config.getCollisionRadius(*this));
|
||||
elephant_foot_increases = foot_increase_radius / (config.branch_radius * (config.diameter_scale_bp_radius - config.diameter_angle_scale_factor)); // elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value.
|
||||
// elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch
|
||||
// the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value.
|
||||
elephant_foot_increases = foot_increase_radius / (config.branch_radius * (config.diameter_scale_bp_radius - config.diameter_angle_scale_factor));
|
||||
}
|
||||
|
||||
|
||||
// set last settings to the best out of both parents. If this is wrong, it will only cause a small performance penalty instead of weird behavior.
|
||||
last_area_increase = {
|
||||
std::min(first.last_area_increase.type, second.last_area_increase.type),
|
||||
|
@ -398,9 +399,7 @@ public:
|
|||
// When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size
|
||||
// This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance.
|
||||
if (has_to_rely_on_min_xy_dist_only)
|
||||
{
|
||||
xy_min_distance = std::max(coord_t(100), xy_min_distance); // If set to low rounding errors WILL cause errors. Best to keep it above 25.
|
||||
}
|
||||
|
||||
xy_distance = std::max(xy_distance, xy_min_distance);
|
||||
|
||||
|
@ -678,7 +677,7 @@ public:
|
|||
*/
|
||||
[[nodiscard]] inline coord_t recommendedMinRadius(LayerIndex layer_idx) const
|
||||
{
|
||||
double scale = (layer_start_bp_radius - layer_idx) * diameter_scale_bp_radius;
|
||||
double scale = (layer_start_bp_radius - int(layer_idx)) * diameter_scale_bp_radius;
|
||||
return scale > 0 ? branch_radius + branch_radius * scale : 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue