Organic Supports improvements:

Added support_tree_branch_distance parameter to UI
Fixed error in calculation of placeable areas, which made some trees to cut through an object.
Locked the tree tips against smoothing of their centerline path.
Reduced density of tips with zero interface layers (see continuous_tips).
Reduced default support_tree_top_rate to 15%
Refactored placement of interfaces for readability.
This commit is contained in:
Vojtech Bubnik 2023-03-10 09:42:06 +01:00
parent ca0b218a56
commit 9ce81d6d12
10 changed files with 666 additions and 474 deletions

View File

@ -452,7 +452,7 @@ static std::vector<std::string> s_Preset_print_options {
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_contact_distance", "support_material_bottom_contact_distance",
"support_material_buildplate_only",
"support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_top_rate", "support_tree_tip_diameter",
"support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter",
"dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder",
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",

View File

@ -2910,6 +2910,18 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(5));
// Tree Support Branch Distance
// How far apart the branches need to be when they touch the model. Making this distance small will cause
// the tree support to touch the model at more points, causing better overhang but making support harder to remove.
def = this->add("support_tree_branch_distance", coFloat);
def->label = L("Branch Distance");
def->category = L("Support material");
def->tooltip = L("How far apart the branches need to be when they touch the model. "
"Making this distance small will cause the tree support to touch the model at more points, "
"causing better overhang but making support harder to remove.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1.));
def = this->add("support_tree_top_rate", coPercent);
def->label = L("Branch Density");
def->category = L("Support material");
@ -2921,7 +2933,7 @@ void PrintConfigDef::init_fff_params()
def->min = 5;
def->max_literal = 35;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPercent(30));
def->set_default_value(new ConfigOptionPercent(15));
def = this->add("temperature", coInts);
def->label = L("Other layers");

View File

@ -555,6 +555,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, support_tree_branch_diameter))
((ConfigOptionFloat, support_tree_branch_diameter_angle))
((ConfigOptionPercent, support_tree_top_rate))
((ConfigOptionFloat, support_tree_branch_distance))
((ConfigOptionFloat, support_tree_tip_diameter))
// The rest
((ConfigOptionBool, thick_bridges))

View File

@ -693,6 +693,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_tree_branch_diameter"
|| opt_key == "support_tree_branch_diameter_angle"
|| opt_key == "support_tree_top_rate"
|| opt_key == "support_tree_branch_distance"
|| opt_key == "support_tree_tip_diameter"
|| opt_key == "raft_expansion"
|| opt_key == "raft_first_layer_density"

View File

@ -89,7 +89,7 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr
// this->minimum_support_area =
// this->minimum_bottom_area =
// this->support_offset =
// this->support_tree_branch_distance = 2.5 * line_width ??
this->support_tree_branch_distance = scaled<coord_t>(config.support_tree_branch_distance.value);
this->support_tree_angle = std::clamp<double>(config.support_tree_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_angle_slow = std::clamp<double>(config.support_tree_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
this->support_tree_branch_diameter = scaled<coord_t>(config.support_tree_branch_diameter.value);
@ -329,6 +329,7 @@ void TreeModelVolumes::precalculate(const PrintObject& print_object, const coord
for (int k = int(j - 1); k >= int(i); -- k) {
std::string legend = format("radius-%1%", unscaled<float>(sorted[k].first.first));
expolygons_with_attributes.push_back({ union_ex(sorted[k].second), SVG::ExPolygonAttributes(legend, std::string(colors[(k - int(i)) % num_colors]), 1.) });
SVG::export_expolygons(debug_out_path("treesupport_cache-%s-%d-%s.svg", name.data(), sorted[i].first.second, legend.c_str()), { expolygons_with_attributes.back() });
}
// Render the range of per radius collision polygons into a common SVG.
SVG::export_expolygons(debug_out_path("treesupport_cache-%s-%d.svg", name.data(), sorted[i].first.second), expolygons_with_attributes);
@ -416,9 +417,6 @@ const Polygons& TreeModelVolumes::getAvoidance(const coord_t orig_radius, LayerI
const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, LayerIndex layer_idx, std::function<void()> throw_on_cancel) const
{
if (orig_radius == 0)
return this->getCollision(0, layer_idx, true);
const coord_t radius = ceilRadius(orig_radius);
if (std::optional<std::reference_wrapper<const Polygons>> result = m_placeable_areas_cache.getArea({ radius, layer_idx }); result)
return (*result).get();
@ -426,7 +424,11 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
tree_supports_show_error("Not precalculated Placeable areas requested."sv, false);
}
const_cast<TreeModelVolumes*>(this)->calculatePlaceables(radius, layer_idx, throw_on_cancel);
if (orig_radius == 0)
// Placable areas for radius 0 are calculated in the general collision code.
return this->getCollision(0, layer_idx, true);
else
const_cast<TreeModelVolumes*>(this)->calculatePlaceables(radius, layer_idx, throw_on_cancel);
return getPlaceableAreas(orig_radius, layer_idx, throw_on_cancel);
}
@ -470,6 +472,7 @@ void TreeModelVolumes::calculateCollision(const std::vector<RadiusLayerPair> &ke
});
}
// Calculate collisions and placable areas for radius and for layer 0 to max_layer_idx inclusive.
void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex max_layer_idx, std::function<void()> throw_on_cancel)
{
// assert(radius == this->ceilRadius(radius));
@ -480,12 +483,14 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
std::sort(layer_outline_indices.begin(), layer_outline_indices.end(),
[this](size_t i, size_t j) { return m_layer_outlines[i].second.size() < m_layer_outlines[j].second.size(); });
const LayerIndex min_layer_last = m_collision_cache.getMaxCalculatedLayer(radius);
std::vector<Polygons> data(max_layer_idx + 1 - min_layer_last, Polygons{});
// Layer range for which the collisions will be calculated.
LayerPolygonCache data;
data.allocate(m_collision_cache.getMaxCalculatedLayer(radius) + 1, max_layer_idx + 1);
const bool calculate_placable = m_support_rests_on_model && radius == 0;
std::vector<Polygons> data_placeable;
LayerPolygonCache data_placeable;
if (calculate_placable)
data_placeable = std::vector<Polygons>(max_layer_idx + 1 - min_layer_last, Polygons{});
data_placeable.allocate(data.idx_begin, data.idx_end);
for (size_t outline_idx : layer_outline_indices)
if (const std::vector<Polygons> &outlines = m_layer_outlines[outline_idx].second; ! outlines.empty()) {
@ -493,8 +498,6 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
const coord_t layer_height = settings.layer_height;
const int z_distance_bottom_layers = int(round(double(settings.support_bottom_distance) / double(layer_height)));
const int z_distance_top_layers = int(round(double(settings.support_top_distance) / double(layer_height)));
const LayerIndex max_required_layer = std::min<LayerIndex>(outlines.size(), max_layer_idx + std::max(coord_t(1), z_distance_top_layers));
const LayerIndex min_layer_bottom = std::max<LayerIndex>(0, min_layer_last - int(z_distance_bottom_layers));
const coord_t xy_distance = outline_idx == m_current_outline_idx ? m_current_min_xy_dist :
// technically this causes collision for the normal xy_distance to be larger by m_current_min_xy_dist_delta for all
// not currently processing meshes as this delta will be added at request time.
@ -505,35 +508,40 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
settings.support_xy_distance;
// 1) Calculate offsets of collision areas in parallel.
std::vector<Polygons> collision_areas_offsetted(max_required_layer + 1 - min_layer_bottom);
tbb::parallel_for(tbb::blocked_range<LayerIndex>(min_layer_bottom, max_required_layer + 1),
[&outlines, &machine_border = std::as_const(m_machine_border), offset_value = radius + xy_distance, min_layer_bottom, &collision_areas_offsetted, &throw_on_cancel]
LayerPolygonCache collision_areas_offsetted;
collision_areas_offsetted.allocate(
std::max<LayerIndex>(0, data.idx_begin - z_distance_bottom_layers),
std::min<LayerIndex>(outlines.size(), data.idx_end + z_distance_top_layers));
tbb::parallel_for(tbb::blocked_range<LayerIndex>(collision_areas_offsetted.idx_begin, collision_areas_offsetted.idx_end),
[&outlines, &machine_border = std::as_const(m_machine_border), offset_value = radius + xy_distance, &collision_areas_offsetted, &throw_on_cancel]
(const tbb::blocked_range<LayerIndex> &range) {
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
Polygons collision_areas = machine_border;
append(collision_areas, outlines[layer_idx]);
// jtRound is not needed here, as the overshoot can not cause errors in the algorithm, because no assumptions are made about the model.
// if a key does not exist when it is accessed it is added!
collision_areas_offsetted[layer_idx - min_layer_bottom] = offset_value == 0 ? union_(collision_areas) : offset(union_ex(collision_areas), offset_value, ClipperLib::jtMiter, 1.2);
collision_areas_offsetted[layer_idx] = offset_value == 0 ?
union_(collision_areas) :
offset(union_ex(collision_areas), offset_value, ClipperLib::jtMiter, 1.2);
throw_on_cancel();
}
});
// 2) Sum over top / bottom ranges.
const bool last = outline_idx == layer_outline_indices.size();
tbb::parallel_for(tbb::blocked_range<LayerIndex>(min_layer_last + 1, max_layer_idx + 1),
[&collision_areas_offsetted, &outlines, &machine_border = m_machine_border, &anti_overhang = m_anti_overhang, min_layer_bottom, radius,
xy_distance, z_distance_bottom_layers, z_distance_top_layers, min_resolution = m_min_resolution, &data, min_layer_last, last, &throw_on_cancel]
(const tbb::blocked_range<LayerIndex>& range) {
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++layer_idx) {
const bool processing_last_mesh = outline_idx == layer_outline_indices.size();
tbb::parallel_for(tbb::blocked_range<LayerIndex>(data.idx_begin, data.idx_end),
[&collision_areas_offsetted, &outlines, &machine_border = m_machine_border, &anti_overhang = m_anti_overhang, radius,
xy_distance, z_distance_bottom_layers, z_distance_top_layers, min_resolution = m_min_resolution, &data, processing_last_mesh, &throw_on_cancel]
(const tbb::blocked_range<LayerIndex>& range) {
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
Polygons collisions;
for (int i = -z_distance_bottom_layers; i <= z_distance_top_layers; ++ i) {
int j = layer_idx + i - min_layer_bottom;
if (j >= 0 && j < int(collision_areas_offsetted.size()) && i <= 0)
for (int i = - z_distance_bottom_layers; i <= 0; ++ i)
if (int j = layer_idx + i; collision_areas_offsetted.has(j))
append(collisions, collision_areas_offsetted[j]);
else if (j >= 0 && layer_idx + i < int(outlines.size()) && i > 0) {
for (int i = 1; i <= z_distance_top_layers; ++ i)
if (int j = layer_idx + i; j < int(outlines.size())) {
Polygons collision_areas_original = machine_border;
append(collision_areas_original, outlines[layer_idx + i]);
append(collision_areas_original, outlines[j]);
// If just the collision (including the xy distance) of the layers above is accumulated, it leads to the
// following issue:
@ -566,37 +574,37 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
// the conditional -0.5 ensures that plastic can never touch on the diagonal
// downward when the z_distance_top_layers = 1. It is assumed to be better to
// not support an overhang<90 degree than to risk fusing to it.
collision_areas_original = offset(union_ex(collision_areas_original), radius + required_range_x, ClipperLib::jtMiter, 1.2);
append(collisions, collision_areas_original);
append(collisions, offset(union_ex(collision_areas_original), radius + required_range_x, ClipperLib::jtMiter, 1.2));
}
}
collisions = last && layer_idx < int(anti_overhang.size()) ? union_(collisions, offset(union_ex(anti_overhang[layer_idx]), radius, ClipperLib::jtMiter, 1.2)) : union_(collisions);
auto &dst = data[layer_idx - (min_layer_last + 1)];
if (last) {
collisions = processing_last_mesh && layer_idx < int(anti_overhang.size()) ?
union_(collisions, offset(union_ex(anti_overhang[layer_idx]), radius, ClipperLib::jtMiter, 1.2)) :
union_(collisions);
auto &dst = data[layer_idx];
if (processing_last_mesh) {
if (! dst.empty())
collisions = union_(collisions, dst);
dst = polygons_simplify(collisions, min_resolution);
} else
append(dst, collisions);
append(dst, std::move(collisions));
throw_on_cancel();
}
});
// 3) Optionally calculate placables.
if (calculate_placable) {
// Calculating both the collision areas and placable areas.
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(min_layer_last + 1, z_distance_bottom_layers + 1), max_layer_idx + 1),
[&collision_areas_offsetted, &anti_overhang = m_anti_overhang, min_layer_bottom, z_distance_bottom_layers, last, min_resolution = m_min_resolution, &data_placeable, min_layer_last, &throw_on_cancel]
// Now calculate the placable areas.
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(data.idx_begin, 1), data.idx_end),
[&collision_areas_offsetted, &anti_overhang = m_anti_overhang, processing_last_mesh,
min_resolution = m_min_resolution, &data_placeable, &throw_on_cancel]
(const tbb::blocked_range<LayerIndex>& range) {
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
LayerIndex layer_idx_below = layer_idx - (z_distance_bottom_layers + 1) - min_layer_bottom;
LayerIndex layer_idx_below = layer_idx - 1;
assert(layer_idx_below >= 0);
auto &current = collision_areas_offsetted[layer_idx - min_layer_bottom];
auto &below = collision_areas_offsetted[layer_idx_below];
auto placable = diff(below, layer_idx < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx - (z_distance_bottom_layers + 1)]) : current);
auto &dst = data_placeable[layer_idx - (min_layer_last + 1)];
if (last) {
const Polygons &current = collision_areas_offsetted[layer_idx];
const Polygons &below = collision_areas_offsetted[layer_idx_below];
Polygons placable = diff(below, layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current);
auto &dst = data_placeable[layer_idx];
if (processing_last_mesh) {
if (! dst.empty())
placable = union_(placable, dst);
dst = polygons_simplify(placable, min_resolution);
@ -619,9 +627,9 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
}
#endif
throw_on_cancel();
m_collision_cache.insert(std::move(data), min_layer_last + 1, radius);
m_collision_cache.insert(std::move(data), radius);
if (calculate_placable)
m_placeable_areas_cache.insert(std::move(data_placeable), min_layer_last + 1, radius);
m_placeable_areas_cache.insert(std::move(data_placeable), radius);
}
void TreeModelVolumes::calculateCollisionHolefree(const std::vector<RadiusLayerPair> &keys, std::function<void()> throw_on_cancel)

View File

@ -328,6 +328,26 @@ public:
}
private:
// Caching polygons for a range of layers.
struct LayerPolygonCache {
std::vector<Polygons> polygons;
LayerIndex idx_begin;
LayerIndex idx_end;
void allocate(LayerIndex aidx_begin, LayerIndex aidx_end) {
this->idx_begin = aidx_begin;
this->idx_end = aidx_end;
this->polygons.assign(aidx_end - aidx_begin, {});
}
LayerIndex begin() const { return idx_begin; }
LayerIndex end() const { return idx_end; }
size_t size() const { return polygons.size(); }
bool has(LayerIndex idx) const { return idx >= idx_begin && idx < idx_end; }
Polygons& operator[](LayerIndex idx) { return polygons[idx + idx_begin]; }
};
/*!
* \brief Convenience typedef for the keys to the caches
*/
@ -363,6 +383,13 @@ private:
for (auto &d : in)
m_data[first_layer_idx ++].emplace(radius, std::move(d));
}
void insert(LayerPolygonCache &&in, coord_t radius) {
std::lock_guard<std::mutex> guard(m_mutex);
LayerIndex i = in.idx_begin;
allocate_layers(i + LayerIndex(in.size()));
for (auto &d : in.polygons)
m_data[i ++].emplace(radius, std::move(d));
}
/*!
* \brief Checks a cache for a given RadiusLayerPair and returns it if it is found
* \param key RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer.

File diff suppressed because it is too large Load Diff

View File

@ -122,7 +122,7 @@ struct SupportElementStateBits {
bool use_min_xy_dist : 1;
/*!
* \brief True if this Element or any parent provides support to a support roof.
* \brief True if this Element or any parent (element above) provides support to a support roof.
*/
bool supports_roof : 1;
@ -193,7 +193,7 @@ struct SupportElementState : public SupportElementStateBits
double elephant_foot_increases;
/*!
* \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move.
* \brief The element tries to not move until this dtt is reached, is set to 0 if the element had to move.
*/
uint32_t dont_move_until;
@ -218,6 +218,8 @@ struct SupportElementState : public SupportElementStateBits
dst.skip_ovalisation = false;
return dst;
}
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; }
};
struct SupportElement

View File

@ -291,7 +291,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
(config->opt_bool("support_material") ||
config->opt_int("support_material_enforce_layers") > 0);
for (const std::string& key : { "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter",
"support_tree_branch_diameter_angle", "support_tree_tip_diameter", "support_tree_top_rate" })
"support_tree_branch_diameter_angle", "support_tree_tip_diameter", "support_tree_branch_distance", "support_tree_top_rate" })
toggle_field(key, has_organic_supports);
for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder",

View File

@ -1531,6 +1531,7 @@ void TabPrint::build()
optgroup->append_single_option_line("support_tree_branch_diameter", category_path + "tree_branch_diameter");
optgroup->append_single_option_line("support_tree_branch_diameter_angle", category_path + "tree_branch_diameter_angle");
optgroup->append_single_option_line("support_tree_tip_diameter", category_path + "tree_tip_diameter");
optgroup->append_single_option_line("support_tree_branch_distance", category_path + "tree_branch_distance");
optgroup->append_single_option_line("support_tree_top_rate", category_path + "tree_top_rate");
page = add_options_page(L("Speed"), "time");