mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-08 23:46:24 -06:00
Remove unused Organic tree code
This commit is contained in:
parent
06c809c598
commit
58d524d75f
1 changed files with 4 additions and 639 deletions
|
@ -2776,633 +2776,6 @@ static void create_nodes_from_area(
|
|||
#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>
|
||||
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();
|
||||
|
||||
// ### 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(
|
||||
*print.get_object(processing.second.front()), volumes, config, move_bounds,
|
||||
bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage,
|
||||
throw_on_cancel);
|
||||
}
|
||||
|
||||
remove_undefined_layers();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue