mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-18 09:55:58 +08:00
Convert recursion function into stack calls
This commit is contained in:
parent
4baac072df
commit
5c2e368f7c
@ -2061,14 +2061,22 @@ struct VoronoiGraph::ExPath : public VoronoiGraph::Path
|
|||||||
using SideBranches = std::priority_queue<
|
using SideBranches = std::priority_queue<
|
||||||
VoronoiGraph::Path, std::vector<VoronoiGraph::Path>, OrderLengthFromLongest>;
|
VoronoiGraph::Path, std::vector<VoronoiGraph::Path>, OrderLengthFromLongest>;
|
||||||
using SideBranchesMap = std::map<const VoronoiGraph::Node *, SideBranches>;
|
using SideBranchesMap = std::map<const VoronoiGraph::Node *, SideBranches>;
|
||||||
|
|
||||||
|
// All side branches in Graph under node
|
||||||
|
// Map contains only node, which has side branche(s)
|
||||||
|
// There is NOT empty SideBranches in map !!!
|
||||||
SideBranchesMap side_branches;
|
SideBranchesMap side_branches;
|
||||||
// circles
|
|
||||||
|
// All circles in Graph under node
|
||||||
std::vector<VoronoiGraph::Circle> circles;
|
std::vector<VoronoiGraph::Circle> circles;
|
||||||
|
|
||||||
// every connected circle have two records(firs to second & second to first)
|
// alone circle does'n have record in connected_circle
|
||||||
// index_to_circles i1, i2; --> connected_circle[i1] = i2; connected_circle[i2] = i1;
|
// every connected circle have at least two records(firs to second & second to first)
|
||||||
|
// EXAMPLE with 3 circles(index to circles stored in this->circles are: c1, c2 and c3) connected together
|
||||||
|
// connected_circle[c1] = {c2, c3};
|
||||||
|
// connected_circle[c2] = {c1, c3};
|
||||||
|
// connected_circle[c3] = {c1, c2};
|
||||||
std::map<size_t, std::set<size_t>> connected_circle;
|
std::map<size_t, std::set<size_t>> connected_circle;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ExPath() = default;
|
ExPath() = default;
|
||||||
};
|
};
|
||||||
@ -2429,6 +2437,7 @@ void append_neighbor_branch(VoronoiGraph::ExPath &dst,
|
|||||||
///
|
///
|
||||||
/// !! not work on circles(island with holes)
|
/// !! not work on circles(island with holes)
|
||||||
/// !! need restructuralization by branch from start path
|
/// !! need restructuralization by branch from start path
|
||||||
|
/// !!! RECURRENT cause stack overflow
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="start_path">IN/OUT path in Graph</param>
|
/// <param name="start_path">IN/OUT path in Graph</param>
|
||||||
VoronoiGraph::ExPath create_longest_branch_from_node(
|
VoronoiGraph::ExPath create_longest_branch_from_node(
|
||||||
@ -2442,15 +2451,7 @@ VoronoiGraph::ExPath create_longest_branch_from_node(
|
|||||||
VoronoiGraph::Path act_path = prev_path; // make copy
|
VoronoiGraph::Path act_path = prev_path; // make copy
|
||||||
act_path.append(&node, distance_to_node);
|
act_path.append(&node, distance_to_node);
|
||||||
|
|
||||||
|
VoronoiGraph::ExPath::SideBranches side_branches;
|
||||||
const std::vector<VoronoiGraph::Node::Neighbor> &neighbors = node.neighbors;
|
|
||||||
size_t neighbor_count = neighbors.size();
|
|
||||||
std::priority_queue<VoronoiGraph::Path, std::vector<VoronoiGraph::Path>,
|
|
||||||
OrderLengthFromLongest>
|
|
||||||
side_branches;
|
|
||||||
|
|
||||||
//side_branches.reserve(neighbor_count - 1); // one neighbor is prev node
|
|
||||||
|
|
||||||
|
|
||||||
// skip node on circle when circle start at this node
|
// skip node on circle when circle start at this node
|
||||||
// vector --> multiple cirle could start at same node
|
// vector --> multiple cirle could start at same node
|
||||||
@ -2461,10 +2462,9 @@ VoronoiGraph::ExPath create_longest_branch_from_node(
|
|||||||
|
|
||||||
// when search on cirle store only branches not search for longest Path
|
// when search on cirle store only branches not search for longest Path
|
||||||
std::vector<size_t> circle_indexes; // connected circles
|
std::vector<size_t> circle_indexes; // connected circles
|
||||||
|
|
||||||
std::vector<size_t> end_circle_indexes;
|
std::vector<size_t> end_circle_indexes;
|
||||||
// Depth search for path of all neighbors --> fill paths
|
// Depth search for path of all neighbors --> fill paths
|
||||||
for (const VoronoiGraph::Node::Neighbor &neighbor : neighbors) {
|
for (const VoronoiGraph::Node::Neighbor &neighbor : node.neighbors) {
|
||||||
if (skip_nodes.find(neighbor.node) != skip_nodes.end()) continue;
|
if (skip_nodes.find(neighbor.node) != skip_nodes.end()) continue;
|
||||||
|
|
||||||
// detection of circle
|
// detection of circle
|
||||||
@ -2551,6 +2551,7 @@ VoronoiGraph::ExPath create_longest_branch_from_node(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create result longest path from longest side branch
|
||||||
VoronoiGraph::Path longest_path(std::move(side_branches.top()));
|
VoronoiGraph::Path longest_path(std::move(side_branches.top()));
|
||||||
side_branches.pop();
|
side_branches.pop();
|
||||||
if (!side_branches.empty()) {
|
if (!side_branches.empty()) {
|
||||||
@ -2625,6 +2626,274 @@ void reshape_longest_path(VoronoiGraph::ExPath& path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DTO for process node to depth search to get longest path in voronoi graph
|
||||||
|
/// </summary>
|
||||||
|
struct CallData
|
||||||
|
{
|
||||||
|
// actual proccessed node
|
||||||
|
const VoronoiGraph::Node *node;
|
||||||
|
// distance to this node from input node
|
||||||
|
double distance_to_node;
|
||||||
|
|
||||||
|
// path from start point to this node
|
||||||
|
// last one is actual node
|
||||||
|
VoronoiGraph::Path act_path;
|
||||||
|
|
||||||
|
// skip node when circle start - must end at this node
|
||||||
|
// set --> multiple cirle could start at same node
|
||||||
|
// previous node should be skiped to so it is initialized with it
|
||||||
|
std::set<const VoronoiGraph::Node *> skip_nodes; // circle
|
||||||
|
|
||||||
|
// store all circle indexes this node is lay on
|
||||||
|
// used to create connected circles structure
|
||||||
|
std::vector<size_t> circle_indexes;
|
||||||
|
// When circle_indexes is not empty node lays on circle
|
||||||
|
// and in this node is not searching for longest path only store side
|
||||||
|
// branches(not part of circle)
|
||||||
|
|
||||||
|
// indexes of circle ending in this node(could be more than one)
|
||||||
|
std::vector<size_t> end_circle_indexes;
|
||||||
|
// When end_circle_indexes == circle_indexes
|
||||||
|
// than it is end of circle (multi circle structure) and it is processed
|
||||||
|
|
||||||
|
// contain possible continue path
|
||||||
|
// possible empty
|
||||||
|
VoronoiGraph::ExPath::SideBranches side_branches;
|
||||||
|
|
||||||
|
// result for this node
|
||||||
|
VoronoiGraph::ExPath& result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CallData(VoronoiGraph::ExPath & result,
|
||||||
|
const VoronoiGraph::Node *node,
|
||||||
|
double distance_to_node)
|
||||||
|
: result(result), node(node), distance_to_node(distance_to_node)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class IStackFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using CallStack = std::stack<std::unique_ptr<IStackFunction>>;
|
||||||
|
virtual ~IStackFunction() = default;
|
||||||
|
virtual void process(CallStack& call_stack) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PostProcessNeighbors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// create on stack
|
||||||
|
/// 1 * PostProcessNeighbors
|
||||||
|
/// N * ExpandNode
|
||||||
|
/// </summary>
|
||||||
|
class EvaluateNeighbor : public IStackFunction
|
||||||
|
{
|
||||||
|
std::unique_ptr<PostProcessNeighbors> post_process_neighbor;
|
||||||
|
public:
|
||||||
|
EvaluateNeighbor(
|
||||||
|
VoronoiGraph::ExPath & result,
|
||||||
|
const VoronoiGraph::Node *node,
|
||||||
|
double distance_to_node = 0.,
|
||||||
|
const VoronoiGraph::Path &prev_path = VoronoiGraph::Path({}, 0.));
|
||||||
|
virtual void process(CallStack &call_stack);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// call after all neighbors are processed
|
||||||
|
/// </summary>
|
||||||
|
class PostProcessNeighbors : public CallData , public IStackFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PostProcessNeighbors(
|
||||||
|
VoronoiGraph::ExPath & result,
|
||||||
|
const VoronoiGraph::Node *node,
|
||||||
|
double distance_to_node = 0.,
|
||||||
|
VoronoiGraph::Path prev_path = VoronoiGraph::Path({}, 0.) // make copy
|
||||||
|
): CallData(result, node, distance_to_node)
|
||||||
|
{
|
||||||
|
const VoronoiGraph::Node *prev_node =
|
||||||
|
(prev_path.path.size() >= 1) ? prev_path.path.back() : nullptr;
|
||||||
|
skip_nodes = {prev_node};
|
||||||
|
// append actual node
|
||||||
|
act_path = std::move(prev_path);
|
||||||
|
act_path.append(node, distance_to_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void process([[maybe_unused]] CallStack &call_stack)
|
||||||
|
{
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
|
||||||
|
void process()
|
||||||
|
{
|
||||||
|
// remember connected circle
|
||||||
|
if (circle_indexes.size() > 1) {
|
||||||
|
for (size_t circle_index : circle_indexes) {
|
||||||
|
for (size_t circle_index2 : circle_indexes) {
|
||||||
|
if (circle_index == circle_index2) continue;
|
||||||
|
result.connected_circle[circle_index].insert(
|
||||||
|
circle_index2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// detect end of circles in this node
|
||||||
|
if (!end_circle_indexes.empty() &&
|
||||||
|
end_circle_indexes.size() == circle_indexes.size()) {
|
||||||
|
size_t circle_index = circle_indexes
|
||||||
|
.front(); // possible any of them
|
||||||
|
side_branches.push(
|
||||||
|
find_longest_path_on_circles(*node, circle_index, result));
|
||||||
|
|
||||||
|
circle_indexes.clear(); // resolved circles
|
||||||
|
}
|
||||||
|
|
||||||
|
// simple node on circle --> only input and output neighbor
|
||||||
|
if (side_branches.empty()) return;
|
||||||
|
|
||||||
|
// is node on unresolved circle?
|
||||||
|
if (!circle_indexes.empty()) {
|
||||||
|
// not search for longest path, it will eval on end of circle
|
||||||
|
result.side_branches[node] = side_branches;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create result longest path from longest side branch
|
||||||
|
VoronoiGraph::Path longest_path(std::move(side_branches.top()));
|
||||||
|
side_branches.pop();
|
||||||
|
if (!side_branches.empty()) {
|
||||||
|
result.side_branches[node] = side_branches;
|
||||||
|
}
|
||||||
|
longest_path.path.insert(longest_path.path.begin(), node);
|
||||||
|
result.path = std::move(longest_path.path);
|
||||||
|
result.length = distance_to_node + longest_path.length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decimate data from Ex path to path
|
||||||
|
/// Done after ONE neighbor is procceessed.
|
||||||
|
/// Check if node is on circle.
|
||||||
|
/// Remember ended circle
|
||||||
|
/// Merge side branches and circle information into result
|
||||||
|
/// </summary>
|
||||||
|
class PostProcessNeighbor : public IStackFunction
|
||||||
|
{
|
||||||
|
CallData &data;
|
||||||
|
public:
|
||||||
|
VoronoiGraph::ExPath neighbor_path; // data filled in EvaluateNeighbor
|
||||||
|
PostProcessNeighbor(CallData &data): data(data) {}
|
||||||
|
|
||||||
|
virtual void process([[maybe_unused]] CallStack &call_stack)
|
||||||
|
{
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
|
||||||
|
void process(){
|
||||||
|
bool is_circle_neighbor = false;
|
||||||
|
if (neighbor_path.path.empty()) { // neighbor node is on circle
|
||||||
|
for (VoronoiGraph::Circle &circle : neighbor_path.circles) {
|
||||||
|
const auto &circle_item = std::find(circle.path.begin(),
|
||||||
|
circle.path.end(), data.node);
|
||||||
|
if (circle_item == circle.path.end())
|
||||||
|
continue; // node is NOT on circle
|
||||||
|
|
||||||
|
size_t next_circle_index = &circle -
|
||||||
|
&neighbor_path.circles.front();
|
||||||
|
size_t circle_index = data.result.circles.size() +
|
||||||
|
next_circle_index;
|
||||||
|
data.circle_indexes.push_back(circle_index);
|
||||||
|
|
||||||
|
// check if this node is end of circle
|
||||||
|
if (circle_item == circle.path.begin()) {
|
||||||
|
data.end_circle_indexes.push_back(circle_index);
|
||||||
|
|
||||||
|
// !! this FIX circle lenght because at detection of
|
||||||
|
// circle it will cost time to calculate it
|
||||||
|
circle.length -= data.act_path.length;
|
||||||
|
|
||||||
|
// skip twice checking of circle
|
||||||
|
data.skip_nodes.insert(circle.path.back());
|
||||||
|
}
|
||||||
|
is_circle_neighbor = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
append_neighbor_branch(data.result, neighbor_path);
|
||||||
|
if (!is_circle_neighbor)
|
||||||
|
data.side_branches.push(std::move(neighbor_path));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExpandNeighbor: public IStackFunction
|
||||||
|
{
|
||||||
|
CallData &data;
|
||||||
|
const VoronoiGraph::Node::Neighbor &neighbor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ExpandNeighbor(CallData & data,
|
||||||
|
const VoronoiGraph::Node::Neighbor &neighbor)
|
||||||
|
: data(data), neighbor(neighbor)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void process(CallStack &call_stack)
|
||||||
|
{
|
||||||
|
if (data.skip_nodes.find(neighbor.node) != data.skip_nodes.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// detection of circle
|
||||||
|
auto circle_opt = create_circle(data.act_path, neighbor);
|
||||||
|
if (circle_opt.has_value()) {
|
||||||
|
size_t circle_index = data.result.circles.size();
|
||||||
|
data.circle_indexes.push_back(circle_index);
|
||||||
|
data.result.circles.emplace_back(std::move(circle_opt.value()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create copy of path(not circles, not side_branches)
|
||||||
|
const VoronoiGraph::Node &next_node = *neighbor.node;
|
||||||
|
// is next node leaf ?
|
||||||
|
if (next_node.neighbors.size() == 1) {
|
||||||
|
VoronoiGraph::Path side_branch({&next_node}, neighbor.edge_length);
|
||||||
|
data.side_branches.push(std::move(side_branch));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto post_process_neighbor = std::make_unique<PostProcessNeighbor>(
|
||||||
|
data);
|
||||||
|
VoronoiGraph::ExPath &neighbor_path = post_process_neighbor
|
||||||
|
->neighbor_path;
|
||||||
|
|
||||||
|
call_stack.emplace(std::move(post_process_neighbor));
|
||||||
|
call_stack.emplace(
|
||||||
|
std::make_unique<EvaluateNeighbor>(
|
||||||
|
neighbor_path,
|
||||||
|
neighbor.node,
|
||||||
|
neighbor.edge_length,
|
||||||
|
data.act_path));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
EvaluateNeighbor::EvaluateNeighbor(VoronoiGraph::ExPath & result,
|
||||||
|
const VoronoiGraph::Node *node,
|
||||||
|
double distance_to_node,
|
||||||
|
const VoronoiGraph::Path &prev_path)
|
||||||
|
: post_process_neighbor(
|
||||||
|
std::make_unique<PostProcessNeighbors>(result,
|
||||||
|
node,
|
||||||
|
distance_to_node,
|
||||||
|
prev_path))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void EvaluateNeighbor::process(IStackFunction::CallStack &call_stack)
|
||||||
|
{
|
||||||
|
CallData &data = *post_process_neighbor;
|
||||||
|
call_stack.emplace(std::move(post_process_neighbor));
|
||||||
|
for (const VoronoiGraph::Node::Neighbor &neighbor : data.node->neighbors)
|
||||||
|
call_stack.emplace(std::make_unique<ExpandNeighbor>(data, neighbor));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PRIVATE:
|
/// PRIVATE:
|
||||||
/// Extract the longest path from voronoi graph
|
/// Extract the longest path from voronoi graph
|
||||||
@ -2632,11 +2901,33 @@ void reshape_longest_path(VoronoiGraph::ExPath& path) {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="start_path">IN/OUT path in Graph</param>
|
/// <param name="start_path">IN/OUT path in Graph</param>
|
||||||
VoronoiGraph::ExPath create_longest_path(const VoronoiGraph::Node *start_node)
|
VoronoiGraph::ExPath create_longest_path(const VoronoiGraph::Node *start_node)
|
||||||
|
{
|
||||||
|
VoronoiGraph::ExPath longest_path;
|
||||||
|
IStackFunction::CallStack call_stack;
|
||||||
|
call_stack.emplace(std::make_unique<EvaluateNeighbor>(longest_path, start_node));
|
||||||
|
|
||||||
|
// depth search for longest path in graph
|
||||||
|
while (!call_stack.empty()) {
|
||||||
|
std::unique_ptr<IStackFunction> stack_function = std::move(call_stack.top());
|
||||||
|
call_stack.pop();
|
||||||
|
stack_function->process(call_stack);
|
||||||
|
// stack function deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
reshape_longest_path(longest_path);
|
||||||
|
// after reshape it shoud be longest path for whole Voronoi Graph
|
||||||
|
return longest_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PRIVATE:
|
||||||
|
/// Extract the longest path from voronoi graph
|
||||||
|
/// Restructuralize path by branch created from random point
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start_path">IN/OUT path in Graph</param>
|
||||||
|
VoronoiGraph::ExPath create_longest_path_recursive(const VoronoiGraph::Node *start_node)
|
||||||
{
|
{
|
||||||
VoronoiGraph::ExPath longest_path = create_longest_branch_from_node(*start_node);
|
VoronoiGraph::ExPath longest_path = create_longest_branch_from_node(*start_node);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
reshape_longest_path(longest_path);
|
reshape_longest_path(longest_path);
|
||||||
// after reshape it shoud be longest path for whole Voronoi Graph
|
// after reshape it shoud be longest path for whole Voronoi Graph
|
||||||
return longest_path;
|
return longest_path;
|
||||||
@ -2985,7 +3276,7 @@ TEST_CASE("Sample small islands", "[VoronoiSkeleton]")
|
|||||||
{4 * size5, size5},
|
{4 * size5, size5},
|
||||||
{3 * size5, size5}}};
|
{3 * size5, size5}}};
|
||||||
|
|
||||||
size_t count_cirlce_lines = 10; // test stack overfrow
|
size_t count_cirlce_lines = 100; // test stack overfrow
|
||||||
double r_CCW = size / 2;
|
double r_CCW = size / 2;
|
||||||
double r_CW = r_CCW - size / 6;
|
double r_CW = r_CCW - size / 6;
|
||||||
// CCW: couter clock wise, CW: clock wise
|
// CCW: couter clock wise, CW: clock wise
|
||||||
@ -3017,15 +3308,16 @@ TEST_CASE("Sample small islands", "[VoronoiSkeleton]")
|
|||||||
cfg.max_length_for_one_support_point = 3 * size;
|
cfg.max_length_for_one_support_point = 3 * size;
|
||||||
|
|
||||||
ExPolygons islands = {
|
ExPolygons islands = {
|
||||||
triangle
|
double_circle
|
||||||
, square
|
// triangle
|
||||||
, sharp_triangle
|
//, square
|
||||||
, rect
|
//, sharp_triangle
|
||||||
, rect_with_hole
|
//, rect
|
||||||
, triangle_with_hole
|
//, rect_with_hole
|
||||||
, rect_with_4_hole
|
//, triangle_with_hole
|
||||||
, mountains
|
//, rect_with_4_hole
|
||||||
, double_circle
|
//, mountains
|
||||||
|
//, double_circle
|
||||||
//, frog_leg
|
//, frog_leg
|
||||||
};
|
};
|
||||||
for (auto &island : islands) {
|
for (auto &island : islands) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user