Remove unused Organic tree code

This commit is contained in:
Noisyfox 2025-01-26 11:16:02 +08:00
parent 06c809c598
commit 58d524d75f

View file

@ -2776,633 +2776,6 @@ static void create_nodes_from_area(
#endif // NDEBUG #endif // NDEBUG
} }
// For producing circular / elliptical areas from SupportElements (one DrawArea per one SupportElement)
// and for smoothing those areas along the tree branches.
struct DrawArea
{
// Element to be processed.
SupportElement *element;
// Element below, if there is such an element. nullptr if element is a root of a tree.
SupportElement *child_element;
// Polygons to be extruded for this element.
Polygons polygons;
};
/*!
* \brief Draws circles around result_on_layer points of the influence areas
*
* \param linear_data[in] All currently existing influence areas with the layer they are on
* \param layer_tree_polygons[out] Resulting branch areas with the layerindex they appear on. layer_tree_polygons.size() has to be at least linear_data.size() as each Influence area in linear_data will save have at least one (that's why it's a vector<vector>) corresponding branch area in layer_tree_polygons.
* \param inverse_tree_order[in] A mapping that returns the child of every influence area.
*/
static void generate_branch_areas(
const TreeModelVolumes &volumes,
const TreeSupportSettings &config,
const std::vector<SupportElements> &move_bounds,
std::vector<DrawArea> &linear_data,
std::function<void()> throw_on_cancel)
{
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC;
constexpr int progress_report_steps = 10;
const size_t progress_inserts_check_interval = linear_data.size() / progress_report_steps;
std::mutex critical_sections;
#endif // SLIC3R_TREESUPPORTS_PROGRESS
// Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time.
const Polygon branch_circle = make_circle(config.branch_radius, SUPPORT_TREE_CIRCLE_RESOLUTION);
tbb::parallel_for(tbb::blocked_range<size_t>(0, linear_data.size()),
[&volumes, &config, &move_bounds, &linear_data, &branch_circle, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
DrawArea &draw_area = linear_data[idx];
const LayerIndex layer_idx = draw_area.element->state.layer_idx;
const coord_t radius = support_element_radius(config, *draw_area.element);
bool parent_uses_min = false;
// Calculate multiple ovalized circles, to connect with every parent and child. Also generate regular circle for the current layer. Merge all these into one area.
std::vector<std::pair<Point, coord_t>> movement_directions{ std::pair<Point, coord_t>(Point(0, 0), radius) };
if (! draw_area.element->state.skip_ovalisation) {
if (draw_area.child_element != nullptr) {
const Point movement = draw_area.child_element->state.result_on_layer - draw_area.element->state.result_on_layer;
movement_directions.emplace_back(movement, radius);
}
const SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
for (int32_t parent_idx : draw_area.element->parents) {
const SupportElement &parent = (*layer_above)[parent_idx];
const Point movement = parent.state.result_on_layer - draw_area.element->state.result_on_layer;
//FIXME why max(..., config.support_line_width)?
movement_directions.emplace_back(movement, std::max(support_element_radius(config, parent), config.support_line_width));
parent_uses_min |= parent.state.use_min_xy_dist;
}
}
const Polygons &collision = volumes.getCollision(0, layer_idx, parent_uses_min || draw_area.element->state.use_min_xy_dist);
auto generateArea = [&collision, &draw_area, &branch_circle, branch_radius = config.branch_radius, support_line_width = config.support_line_width, &movement_directions]
(coord_t aoffset, double &max_speed) {
Polygons poly;
max_speed = 0;
for (std::pair<Point, coord_t> movement : movement_directions) {
max_speed = std::max(max_speed, movement.first.cast<double>().norm());
// Visualization: https://jsfiddle.net/0zvcq39L/2/
// Ovalizes the circle to an ellipse, that contains both old center and new target position.
double used_scale = (movement.second + aoffset) / (1.0 * branch_radius);
Point center_position = draw_area.element->state.result_on_layer + movement.first / 2;
const double moveX = movement.first.x() / (used_scale * branch_radius);
const double moveY = movement.first.y() / (used_scale * branch_radius);
const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY));
double matrix[] = {
used_scale * (1 + moveX * moveX * vsize_inv),
used_scale * (0 + moveX * moveY * vsize_inv),
used_scale * (0 + moveX * moveY * vsize_inv),
used_scale * (1 + moveY * moveY * vsize_inv),
};
Polygon circle;
for (Point vertex : branch_circle)
circle.points.emplace_back(center_position + Point(matrix[0] * vertex.x() + matrix[1] * vertex.y(), matrix[2] * vertex.x() + matrix[3] * vertex.y()));
poly.emplace_back(std::move(circle));
}
// 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_clipped(offset(union_(poly), std::min(coord_t(50), support_line_width / 4), jtMiter, 1.2), collision);
return poly;
};
// Ensure branch area will not overlap with model/collision. This can happen because of e.g. ovalization or increase_until_radius.
double max_speed;
Polygons polygons = generateArea(0, max_speed);
const bool fast_relative_movement = max_speed > radius * 0.75;
if (fast_relative_movement || support_element_radius(config, *draw_area.element) - support_element_collision_radius(config, draw_area.element->state) > config.support_line_width) {
// Simulate the path the nozzle will take on the outermost wall.
// If multiple parts exist, the outer line will not go all around the support part potentially causing support material to be printed mid air.
ExPolygons nozzle_path = offset_ex(polygons, - config.support_line_width / 2);
if (nozzle_path.size() > 1) {
// Just try to make the area a tiny bit larger.
polygons = generateArea(config.support_line_width / 2, max_speed);
nozzle_path = offset_ex(polygons, -config.support_line_width / 2);
// If larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best.
if (nozzle_path.size() > 1) {
ExPolygons polygons_with_correct_center;
for (ExPolygon &part : nozzle_path) {
bool drop = false;
if (! part.contains(draw_area.element->state.result_on_layer)) {
// try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors...
Point pt = draw_area.element->state.result_on_layer;
move_inside(to_polygons(part), pt, 0);
drop = (draw_area.element->state.result_on_layer - pt).cast<double>().norm() >= scaled<double>(0.025);
}
if (! drop)
polygons_with_correct_center.emplace_back(std::move(part));
}
// Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above.
assert(contains(polygons, draw_area.element->state.result_on_layer));
polygons = diff_clipped(offset(polygons_with_correct_center, config.support_line_width / 2, jtMiter, 1.2),
//FIXME Vojtech: Clipping may split the region into multiple pieces again, reversing the fixing effort.
collision);
}
}
}
draw_area.polygons = std::move(polygons);
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
if (idx % progress_inserts_check_interval == 0) {
std::lock_guard<std::mutex> critical_section_progress(critical_sections);
progress_total += TREE_PROGRESS_GENERATE_BRANCH_AREAS / progress_report_steps;
Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
}
#endif
throw_on_cancel();
}
});
}
/*!
* \brief Applies some smoothing to the outer wall, intended to smooth out sudden jumps as they can happen when a branch moves though a hole.
*
* \param layer_tree_polygons[in,out] Resulting branch areas with the layerindex they appear on.
*/
static void smooth_branch_areas(
const TreeSupportSettings &config,
std::vector<SupportElements> &move_bounds,
std::vector<DrawArea> &linear_data,
const std::vector<size_t> &linear_data_layers,
std::function<void()> throw_on_cancel)
{
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS;
#endif // SLIC3R_TREESUPPORTS_PROGRESS
const coord_t max_radius_change_per_layer = 1 + 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(move_bounds.size()) - 1; ++ layer_idx) {
const size_t processing_base = linear_data_layers[layer_idx];
const size_t processing_base_above = linear_data_layers[layer_idx + 1];
const SupportElements &layer_above = move_bounds[layer_idx + 1];
tbb::parallel_for(tbb::blocked_range<size_t>(0, processing_base_above - processing_base),
[&](const tbb::blocked_range<size_t> &range) {
for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) {
DrawArea &draw_area = linear_data[processing_base + processing_idx];
assert(draw_area.element->state.layer_idx == layer_idx);
double max_outer_wall_distance = 0;
bool do_something = false;
for (int32_t parent_idx : draw_area.element->parents) {
const SupportElement &parent = layer_above[parent_idx];
assert(parent.state.layer_idx == layer_idx + 1);
if (support_element_radius(config, parent) != support_element_collision_radius(config, parent)) {
do_something = true;
max_outer_wall_distance = std::max(max_outer_wall_distance,
(draw_area.element->state.result_on_layer - parent.state.result_on_layer).cast<double>().norm() - (support_element_radius(config, *draw_area.element) - support_element_radius(config, parent)));
}
}
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) {
assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer));
Polygons max_allowed_area = offset(draw_area.polygons, float(max_outer_wall_distance), jtMiter, 1.2);
for (int32_t parent_idx : draw_area.element->parents) {
const SupportElement &parent = layer_above[parent_idx];
#ifndef NDEBUG
assert(parent.state.layer_idx == layer_idx + 1);
assert(contains(linear_data[processing_base_above + parent_idx].polygons, parent.state.result_on_layer));
double radius_increase = support_element_radius(config, *draw_area.element) - support_element_radius(config, parent);
assert(radius_increase >= 0);
double shift = (draw_area.element->state.result_on_layer - parent.state.result_on_layer).cast<double>().norm();
assert(shift < radius_increase + 2. * config.maximum_move_distance_slow);
#endif // NDEBUG
if (support_element_radius(config, parent) != support_element_collision_radius(config, parent)) {
// No other element on this layer than the current one may be connected to &parent,
// thus it is safe to update parent's DrawArea directly.
Polygons &dst = linear_data[processing_base_above + parent_idx].polygons;
// Polygons orig = dst;
if (! dst.empty()) {
dst = intersection(dst, max_allowed_area);
#if 0
if (dst.empty()) {
static int irun = 0;
SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-smooth-error-%d.svg", irun ++),
{ { { union_ex(max_allowed_area) }, { "max_allowed_area", "yellow", 0.5f } },
{ { union_ex(orig) }, { "orig", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
::MessageBoxA(nullptr, "TreeSupport smoothing bug", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
}
#endif
}
}
}
}
throw_on_cancel();
}
});
}
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
progress_total += TREE_PROGRESS_SMOOTH_BRANCH_AREAS / 2;
Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); // It is just assumed that both smoothing loops together are one third of the time spent in this function. This was guessed. As the whole function is only 10%, and the smoothing is hard to predict a progress report in the loop may be not useful.
#endif
// smooth downwards
for (auto& element : move_bounds.back())
element.state.marked = false;
for (int layer_idx = int(move_bounds.size()) - 2; layer_idx >= 0; -- layer_idx) {
const size_t processing_base = linear_data_layers[layer_idx];
const size_t processing_base_above = linear_data_layers[layer_idx + 1];
const SupportElements &layer_above = move_bounds[layer_idx + 1];
tbb::parallel_for(tbb::blocked_range<size_t>(0, processing_base_above - processing_base),
[&](const tbb::blocked_range<size_t> &range) {
for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) {
DrawArea &draw_area = linear_data[processing_base + processing_idx];
bool do_something = false;
Polygons max_allowed_area;
for (int32_t parent_idx : draw_area.element->parents) {
const SupportElement &parent = layer_above[parent_idx];
coord_t max_outer_line_increase = max_radius_change_per_layer;
Polygons result = offset(linear_data[processing_base_above + parent_idx].polygons, max_outer_line_increase, jtMiter, 1.2);
Point direction = draw_area.element->state.result_on_layer - parent.state.result_on_layer;
// move the polygons object
for (auto &outer : result)
for (Point& p : outer)
p += direction;
append(max_allowed_area, std::move(result));
do_something = do_something || parent.state.marked || support_element_collision_radius(config, parent) != support_element_radius(config, parent);
}
if (do_something) {
// Trim the current drawing areas with max_allowed_area.
Polygons result = intersection(max_allowed_area, draw_area.polygons);
if (area(result) < area(draw_area.polygons)) {
// Mark parent as modified to propagate down.
draw_area.element->state.marked = true;
draw_area.polygons = std::move(result);
}
}
throw_on_cancel();
}
});
}
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
progress_total += TREE_PROGRESS_SMOOTH_BRANCH_AREAS / 2;
Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
#endif
}
/*!
* \brief Drop down areas that do rest non-gracefully on the model to ensure the branch actually rests on something.
*
* \param layer_tree_polygons[in] Resulting branch areas with the layerindex they appear on.
* \param linear_data[in] All currently existing influence areas with the layer they are on
* \param dropped_down_areas[out] Areas that have to be added to support all non-graceful areas.
* \param inverse_tree_order[in] A mapping that returns the child of every influence area.
*/
static void drop_non_gracious_areas(
const TreeModelVolumes &volumes,
const std::vector<DrawArea> &linear_data,
std::vector<Polygons> &support_layer_storage,
std::function<void()> throw_on_cancel)
{
const auto _tiny_area_threshold = tiny_area_threshold();
std::vector<std::vector<std::pair<LayerIndex, Polygons>>> dropped_down_areas(linear_data.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, linear_data.size()),
[&](const tbb::blocked_range<size_t> &range) {
for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
// If a element has no child, it connects to whatever is below as no support further down for it will exist.
if (const DrawArea &draw_element = linear_data[idx]; ! draw_element.element->state.to_model_gracious && draw_element.child_element == nullptr) {
Polygons rest_support;
const LayerIndex layer_idx_first = draw_element.element->state.layer_idx - 1;
for (LayerIndex layer_idx = layer_idx_first; area(rest_support) > _tiny_area_threshold && layer_idx >= 0; -- layer_idx) {
rest_support = diff_clipped(layer_idx == layer_idx_first ? draw_element.polygons : rest_support, volumes.getCollision(0, layer_idx, false));
dropped_down_areas[idx].emplace_back(layer_idx, rest_support);
}
}
throw_on_cancel();
}
});
for (coord_t i = 0; i < static_cast<coord_t>(dropped_down_areas.size()); i++)
for (std::pair<LayerIndex, Polygons> &pair : dropped_down_areas[i])
append(support_layer_storage[pair.first], std::move(pair.second));
}
/*!
* \brief Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as support to storage
*
* \param support_layer_storage[in] Areas where support should be generated.
* \param support_roof_storage[in] Areas where support was replaced with roof.
* \param storage[in,out] The storage where the support should be stored.
*/
static void finalize_interface_and_support_areas(
const PrintObject &print_object,
const TreeModelVolumes &volumes,
const TreeSupportSettings &config,
const std::vector<Polygons> &overhangs,
std::vector<Polygons> &support_layer_storage,
std::vector<Polygons> &support_roof_storage,
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage,
std::function<void()> throw_on_cancel)
{
assert(std::all_of(bottom_contacts.begin(), bottom_contacts.end(), [](auto *p) { return p == nullptr; }));
// assert(std::all_of(top_contacts.begin(), top_contacts.end(), [](auto* p) { return p == nullptr; }));
assert(std::all_of(intermediate_layers.begin(), intermediate_layers.end(), [](auto* p) { return p == nullptr; }));
InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::InterfaceAreaOverwritesSupport;
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS + TREE_PROGRESS_SMOOTH_BRANCH_AREAS;
#endif // SLIC3R_TREESUPPORTS_PROGRESS
// Iterate over the generated circles in parallel and clean them up. Also add support floor.
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) {
// Subtract support lines of the branches from the roof
SupportGeneratorLayer *support_roof = top_contacts[layer_idx];
Polygons support_roof_polygons;
if (Polygons &src = support_roof_storage[layer_idx]; ! src.empty()) {
if (support_roof != nullptr && ! support_roof->polygons.empty()) {
support_roof_polygons = union_(src, support_roof->polygons);
support_roof->polygons.clear();
} else
support_roof_polygons = std::move(src);
} else if (support_roof != nullptr) {
support_roof_polygons = std::move(support_roof->polygons);
support_roof->polygons.clear();
}
assert(intermediate_layers[layer_idx] == nullptr);
Polygons base_layer_polygons = std::move(support_layer_storage[layer_idx]);
if (! base_layer_polygons.empty()) {
// Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned.
base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50);
//smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
// simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution.
base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled<double>(0.03), double(config.resolution)), polygons_strictly_simple);
}
if (! support_roof_polygons.empty() && ! base_layer_polygons.empty()) {
// if (area(intersection(base_layer_polygons, support_roof_polygons)) > tiny_area_threshold)
{
switch (interface_pref) {
case InterfacePreference::InterfaceAreaOverwritesSupport:
base_layer_polygons = diff(base_layer_polygons, support_roof_polygons);
break;
case InterfacePreference::SupportAreaOverwritesInterface:
support_roof_polygons = diff(support_roof_polygons, base_layer_polygons);
break;
//FIXME
#if 1
case InterfacePreference::InterfaceLinesOverwriteSupport:
case InterfacePreference::SupportLinesOverwriteInterface:
assert(false);
[[fallthrough]];
#else
case InterfacePreference::InterfaceLinesOverwriteSupport:
{
// Hatch the support roof interfaces, offset them by their line width and subtract them from support base.
Polygons interface_lines = offset(to_polylines(
generate_support_infill_lines(support_roof->polygons, true, layer_idx, config.support_roof_line_distance)),
config.support_roof_line_width / 2);
base_layer_polygons = diff(base_layer_polygons, interface_lines);
break;
}
case InterfacePreference::SupportLinesOverwriteInterface:
{
// Hatch the support roof interfaces, offset them by their line width and subtract them from support base.
Polygons tree_lines = union_(offset(to_polylines(
generate_support_infill_lines(base_layer_polygons, false, layer_idx, config.support_line_distance, true)),
config.support_line_width / 2));
// do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below.
support_roof->polygons = diff(support_roof->polygons, tree_lines);
break;
}
#endif
case InterfacePreference::Nothing:
break;
}
}
}
// Subtract support floors from the support area and add them to the support floor instead.
if (config.support_bottom_layers > 0 && ! base_layer_polygons.empty()) {
SupportGeneratorLayer*& support_bottom = bottom_contacts[layer_idx];
Polygons layer_outset = diff_clipped(
config.support_bottom_offset > 0 ? offset(base_layer_polygons, config.support_bottom_offset, jtMiter, 1.2) : base_layer_polygons,
volumes.getCollision(0, layer_idx, false));
Polygons floor_layer;
size_t layers_below = 0;
while (layers_below <= config.support_bottom_layers) {
// one sample at 0 layers below, another at config.support_bottom_layers. In-between samples at config.performance_interface_skip_layers distance from each other.
const size_t sample_layer = static_cast<size_t>(std::max(0, (static_cast<int>(layer_idx) - static_cast<int>(layers_below)) - static_cast<int>(config.z_distance_bottom_layers)));
//FIXME subtract the wipe tower
append(floor_layer, intersection(layer_outset, overhangs[sample_layer]));
if (layers_below < config.support_bottom_layers)
layers_below = std::min(layers_below + 1, config.support_bottom_layers);
else
break;
}
if (! floor_layer.empty()) {
if (support_bottom == nullptr)
support_bottom = &layer_allocate(layer_storage, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx);
support_bottom->polygons = union_(floor_layer, support_bottom->polygons);
base_layer_polygons = diff_clipped(base_layer_polygons, offset(support_bottom->polygons, scaled<float>(0.01), jtMiter, 1.2)); // Subtract the support floor from the normal support.
}
}
if (! support_roof_polygons.empty()) {
if (support_roof == nullptr)
support_roof = top_contacts[layer_idx] = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, layer_idx);
support_roof->polygons = union_(support_roof_polygons);
}
if (! base_layer_polygons.empty()) {
SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(layer_storage, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx);
base_layer->polygons = union_(base_layer_polygons);
}
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
{
std::lock_guard<std::mutex> critical_section_progress(critical_sections);
progress_total += TREE_PROGRESS_FINALIZE_BRANCH_AREAS / support_layer_storage.size();
Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
}
#endif
#if 0
{
std::lock_guard<std::mutex> lock(critical_sections);
if (!storage.support.supportLayers[layer_idx].support_infill_parts.empty() || !storage.support.supportLayers[layer_idx].support_roof.empty())
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, static_cast<int>(layer_idx));
}
#endif
throw_on_cancel();
}
});
}
/*!
* \brief Draws circles around result_on_layer points of the influence areas and applies some post processing.
*
* \param move_bounds[in] All currently existing influence areas
* \param storage[in,out] The storage where the support should be stored.
*/
static void draw_areas(
PrintObject &print_object,
const TreeModelVolumes &volumes,
const TreeSupportSettings &config,
const std::vector<Polygons> &overhangs,
std::vector<SupportElements> &move_bounds,
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage,
std::function<void()> throw_on_cancel)
{
std::vector<Polygons> support_layer_storage(move_bounds.size());
std::vector<Polygons> support_roof_storage(move_bounds.size());
// All SupportElements are put into a layer independent storage to improve parallelization.
std::vector<DrawArea> linear_data;
std::vector<size_t> linear_data_layers;
{
std::vector<std::pair<SupportElement*, SupportElement*>> map_downwards_old;
std::vector<std::pair<SupportElement*, SupportElement*>> map_downwards_new;
for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
map_downwards_new.clear();
linear_data_layers.emplace_back(linear_data.size());
std::sort(map_downwards_old.begin(), map_downwards_old.end(), [](auto &l, auto &r) { return l.first < r.first; });
for (SupportElement &elem : move_bounds[layer_idx]) {
SupportElement *child = nullptr;
if (layer_idx > 0) {
auto it = std::lower_bound(map_downwards_old.begin(), map_downwards_old.end(), &elem, [](auto &l, const SupportElement *r) { return l.first < r; });
if (it != map_downwards_old.end() && it->first == &elem) {
child = it->second;
// Only one link points to a node above from below.
assert(! (++ it != map_downwards_old.end() && it->first == &elem));
}
assert(child ? child->state.result_on_layer_is_set() : elem.state.target_height > layer_idx);
}
for (int32_t parent_idx : elem.parents) {
SupportElement &parent = (*layer_above)[parent_idx];
if (parent.state.result_on_layer_is_set())
map_downwards_new.emplace_back(&parent, &elem);
}
linear_data.push_back({ &elem, child });
}
std::swap(map_downwards_old, map_downwards_new);
}
linear_data_layers.emplace_back(linear_data.size());
}
throw_on_cancel();
#ifndef NDEBUG
for (size_t i = 0; i < move_bounds.size(); ++ i) {
size_t begin = linear_data_layers[i];
size_t end = linear_data_layers[i + 1];
for (size_t j = begin; j < end; ++ j)
assert(linear_data[j].element == &move_bounds[i][j - begin]);
}
#endif // NDEBUG
auto t_start = std::chrono::high_resolution_clock::now();
// Generate the circles that will be the branches.
generate_branch_areas(volumes, config, move_bounds, linear_data, throw_on_cancel);
#if 0
assert(linear_data_layers.size() == move_bounds.size() + 1);
for (const auto &draw_area : linear_data)
assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer));
for (size_t i = 0; i < move_bounds.size(); ++ i) {
size_t begin = linear_data_layers[i];
size_t end = linear_data_layers[i + 1];
for (size_t j = begin; j < end; ++ j) {
const auto &draw_area = linear_data[j];
assert(draw_area.element == &move_bounds[i][j - begin]);
assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer));
}
}
#endif
#if 0
for (size_t area_layer_idx = 0; area_layer_idx + 1 < linear_data_layers.size(); ++ area_layer_idx) {
size_t begin = linear_data_layers[area_layer_idx];
size_t end = linear_data_layers[area_layer_idx + 1];
Polygons polygons;
for (size_t area_idx = begin; area_idx < end; ++ area_idx) {
DrawArea &area = linear_data[area_idx];
append(polygons, area.polygons);
}
SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-raw-%d.svg", area_layer_idx),
{ { { union_ex(polygons) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
}
#endif
auto t_generate = std::chrono::high_resolution_clock::now();
// In some edgecases a branch may go though a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be caught and smoothed out.
smooth_branch_areas(config, move_bounds, linear_data, linear_data_layers, throw_on_cancel);
#if 0
for (size_t area_layer_idx = 0; area_layer_idx + 1 < linear_data_layers.size(); ++area_layer_idx) {
size_t begin = linear_data_layers[area_layer_idx];
size_t end = linear_data_layers[area_layer_idx + 1];
Polygons polygons;
for (size_t area_idx = begin; area_idx < end; ++area_idx) {
DrawArea& area = linear_data[area_idx];
append(polygons, area.polygons);
}
SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-smooth-%d.svg", area_layer_idx),
{ { { union_ex(polygons) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
}
#endif
auto t_smooth = std::chrono::high_resolution_clock::now();
// drop down all trees that connect non gracefully with the model
drop_non_gracious_areas(volumes, linear_data, support_layer_storage, throw_on_cancel);
auto t_drop = std::chrono::high_resolution_clock::now();
// Single threaded combining all support areas to the right layers.
{
auto begin = linear_data.begin();
for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
size_t cnt_roofs = 0;
size_t cnt_layers = 0;
auto end = begin;
for (; end != linear_data.end() && end->element->state.layer_idx == layer_idx; ++ end)
++ (end->element->state.missing_roof_layers > end->element->state.distance_to_top ? cnt_roofs : cnt_layers);
auto &this_roofs = support_roof_storage[layer_idx];
auto &this_layers = support_layer_storage[layer_idx];
this_roofs.reserve(this_roofs.size() + cnt_roofs);
this_layers.reserve(this_layers.size() + cnt_layers);
for (auto it = begin; it != end; ++ it)
std::move(std::begin(it->polygons), std::end(it->polygons), std::back_inserter(it->element->state.missing_roof_layers > it->element->state.distance_to_top ? this_roofs : this_layers));
begin = end;
}
}
finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage,
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
auto t_end = std::chrono::high_resolution_clock::now();
auto dur_gen_tips = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_generate - t_start).count();
auto dur_smooth = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_smooth - t_generate).count();
auto dur_drop = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_drop - t_smooth).count();
auto dur_finalize = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_drop).count();
BOOST_LOG_TRIVIAL(info) <<
"Time used for drawing subfuctions: generate_branch_areas: " << dur_gen_tips << " ms "
"smooth_branch_areas: " << dur_smooth << " ms "
"drop_non_gracious_areas: " << dur_drop << " ms "
"finalize_interface_and_support_areas " << dur_finalize << " ms";
}
template<bool flip_normals> template<bool flip_normals>
void triangulate_fan(indexed_triangle_set &its, int ifan, int ibegin, int iend) void triangulate_fan(indexed_triangle_set &its, int ifan, int ibegin, int iend)
{ {
@ -4065,18 +3438,10 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
auto t_place = std::chrono::high_resolution_clock::now(); auto t_place = std::chrono::high_resolution_clock::now();
// ### draw these points as circles // ### draw these points as circles
if (print_object.config().support_style.value != smsOrganic &&
// Orca: use organic as default
print_object.config().support_style.value != smsDefault) {
draw_areas(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds,
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
} else {
organic_draw_branches( organic_draw_branches(
*print.get_object(processing.second.front()), volumes, config, move_bounds, *print.get_object(processing.second.front()), volumes, config, move_bounds,
bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage, bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage,
throw_on_cancel); throw_on_cancel);
}
remove_undefined_layers(); remove_undefined_layers();