ENH: improve smoothness of tree supports

1. Instead of smoothing top-down, now we smooth tree branches bottom-up, and do not stop at merged nodes.
2. Fix a bug where some nodes don't have child. This is critical for bottom-up smoothing.

Change-Id: Iac0fecd81dac541ca390bcf5cbdfe7fd66cfd3a2
(cherry picked from commit 536a8a4f0ef76fa0358f4b4b181c4c7e7fff8bc7)
This commit is contained in:
Arthur 2023-04-13 20:38:41 +08:00 committed by Lane.Wei
parent 763cf8ebfc
commit 719f273fa2
2 changed files with 69 additions and 34 deletions

View file

@ -27,6 +27,9 @@
// #define SUPPORT_TREE_DEBUG_TO_SVG // #define SUPPORT_TREE_DEBUG_TO_SVG
#if SUPPORT_TREE_DEBUG_TO_SVG
#include "nlohmann/json.hpp"
#endif
namespace Slic3r namespace Slic3r
{ {
#define unscale_(val) ((val) * SCALING_FACTOR) #define unscale_(val) ((val) * SCALING_FACTOR)
@ -2983,64 +2986,94 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
void TreeSupport::smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes) void TreeSupport::smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes)
{ {
std::map<Node *, bool> is_processed;
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
std::vector<Node *> &curr_layer_nodes = contact_nodes[layer_nr]; std::vector<Node *> &curr_layer_nodes = contact_nodes[layer_nr];
if (curr_layer_nodes.empty()) continue; if (curr_layer_nodes.empty()) continue;
for (Node *node : curr_layer_nodes) { for (Node *node : curr_layer_nodes) {
is_processed.emplace(node, false); node->is_processed = false;
if (layer_nr == 0) node->is_merged = true; // nodes on plate are also merged nodes if (layer_nr == 0) node->is_merged = true; // nodes on plate are also merged nodes
} }
} }
for (int layer_nr = contact_nodes.size()-1; layer_nr >=0 ; layer_nr--) { for (int layer_nr = 0; layer_nr< contact_nodes.size(); layer_nr++) {
std::vector<Node *> &curr_layer_nodes = contact_nodes[layer_nr]; std::vector<Node *> &curr_layer_nodes = contact_nodes[layer_nr];
if (curr_layer_nodes.empty()) continue; if (curr_layer_nodes.empty()) continue;
for (Node *node : curr_layer_nodes) { for (Node *node : curr_layer_nodes) {
if (!is_processed[node]) { if (!node->is_processed) {
std::vector<Point> pts; std::vector<Point> pts;
std::vector<Node *> branch; std::vector<Node *> branch;
Node * p_node = node; Node * p_node = node;
// add head for second path from the merged node if there are multiple ones // add a fixed head
if (!node->is_merged && node->parent) { if (node->child) {
pts.push_back(p_node->parent->position); pts.push_back(p_node->child->position);
branch.push_back(p_node->parent); branch.push_back(p_node->child);
} }
do { do {
pts.push_back(p_node->position); pts.push_back(p_node->position);
//is_processed[p_node] = true;
branch.push_back(p_node); branch.push_back(p_node);
p_node = p_node->child; p_node = p_node->parent;
} while (p_node && !p_node->is_merged && !is_processed[p_node]); } while (p_node && !p_node->is_processed);
// TODO is it necessary to add tail also?
if (p_node && p_node->is_merged) {
pts.push_back(p_node->position);
branch.push_back(p_node);
}
if (pts.size() < 3) continue; if (pts.size() < 3) continue;
std::vector<Point> pts1 = pts; std::vector<Point> pts1 = pts;
// TODO here we assume layer height gap is constant. If not true, need to consider height jump // TODO here we assume layer height gap is constant. If not true, need to consider height jump
// TODO it seems the smooth iterations can't be larger than 1, otherwise some nodes will fly away const int iterations = 100;
for (size_t k = 0; k < 50; k++) { for (size_t k = 0; k < iterations; k++) {
for (size_t i = 1; i < pts.size() - 1; i++) { for (size_t i = 1; i < pts.size() - 1; i++) {
size_t i2 = i >= 2 ? i - 2 : 0; size_t i2 = i >= 2 ? i - 2 : 0;
size_t i3 = i < pts.size() - 2 ? i + 2 : pts.size() - 1; size_t i3 = i < pts.size() - 2 ? i + 2 : pts.size() - 1;
Point pt = (pts[i2] + pts[i - 1] + pts[i] + pts[i + 1] + pts[i3]) / 5; Point pt = (pts[i2] + pts[i - 1] + pts[i] + pts[i + 1] + pts[i3]) / 5;
pts1[i] = pt; pts1[i] = pt;
if (k == iterations - 1) {
branch[i]->position = pt;
branch[i]->movement = (pts[i + 1] - pts[i - 1]) / 2;
branch[i]->is_processed = true;
}
} }
std::swap(pts, pts1); if (k < iterations - 1)
} std::swap(pts, pts1);
for (size_t i = 1; i < pts.size() - 1; i++) {
if (!is_processed[branch[i]]) {
branch[i]->position = pts[i];
branch[i]->movement = branch[i]->parent ? (branch[i]->position - branch[i]->parent->position) : Point(0, 0);
is_processed[branch[i]] = true;
}
} }
} }
} }
} }
#if SUPPORT_TREE_DEBUG_TO_SVG
// save tree structure for viewing in python
struct TreeNode {
Vec3f pos;
std::vector<int> children; // index of children in the storing vector
TreeNode(Point pt, float z) {
pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z };
}
};
std::vector<TreeNode> tree_nodes;
std::map<Node*, int> ptr2idx;
std::map<int, Node*> idx2ptr;
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
for (Node* node : curr_layer_nodes) {
ptr2idx.emplace(node, tree_nodes.size());
idx2ptr.emplace(tree_nodes.size(), node);
tree_nodes.emplace_back(node->position, node->print_z);
}
}
for (size_t i = 0; i < tree_nodes.size(); i++) {
TreeNode& tree_node = tree_nodes[i];
Node* p_node = idx2ptr[i];
if (p_node->child)
tree_node.children.push_back(ptr2idx[p_node->child]);
}
nlohmann::json jj;
for (size_t i = 0; i < tree_nodes.size(); i++) {
nlohmann::json j;
j["pos"] = tree_nodes[i].pos;
j["children"] = tree_nodes[i].children;
jj.push_back(j);
}
std::ofstream ofs("tree_nodes.json");
ofs << jj.dump();
ofs.close();
#endif
} }
void TreeSupport::adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes) void TreeSupport::adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes)

View file

@ -259,6 +259,8 @@ public:
if (dist_mm_to_top==0) if (dist_mm_to_top==0)
dist_mm_to_top = parent->dist_mm_to_top + parent->height; dist_mm_to_top = parent->dist_mm_to_top + parent->height;
parent->child = this; parent->child = this;
for (auto& neighbor : parent->merged_neighbours)
neighbor->child = this;
} }
} }
@ -280,15 +282,15 @@ public:
/*! /*!
* \brief The position of this node on the layer. * \brief The position of this node on the layer.
*/ */
Point position; Point position;
Point movement; // movement towards neighbor center or outline
Point movement; // movement towards neighbor center or outline mutable double radius = 0.0;
mutable double radius = 0.0; mutable double max_move_dist = 0.0;
mutable double max_move_dist = 0.0; NodeType type = eCircle;
NodeType type = eCircle; bool is_merged = false; // this node is generated by merging upper nodes
bool is_merged = false; // this node is generated by merging upper nodes
bool is_corner = false; bool is_corner = false;
const ExPolygon* overhang = nullptr; // when type==ePolygon, set this value to get original overhang area bool is_processed = false;
const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area
/*! /*!
* \brief The direction of the skin lines above the tip of the branch. * \brief The direction of the skin lines above the tip of the branch.