mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-19 06:35:52 +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<
|
||||
VoronoiGraph::Path, std::vector<VoronoiGraph::Path>, OrderLengthFromLongest>;
|
||||
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;
|
||||
// circles
|
||||
|
||||
// All circles in Graph under node
|
||||
std::vector<VoronoiGraph::Circle> circles;
|
||||
|
||||
// every connected circle have two records(firs to second & second to first)
|
||||
// index_to_circles i1, i2; --> connected_circle[i1] = i2; connected_circle[i2] = i1;
|
||||
// alone circle does'n have record in connected_circle
|
||||
// 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;
|
||||
|
||||
public:
|
||||
ExPath() = default;
|
||||
};
|
||||
@ -2429,6 +2437,7 @@ void append_neighbor_branch(VoronoiGraph::ExPath &dst,
|
||||
///
|
||||
/// !! not work on circles(island with holes)
|
||||
/// !! need restructuralization by branch from start path
|
||||
/// !!! RECURRENT cause stack overflow
|
||||
/// </summary>
|
||||
/// <param name="start_path">IN/OUT path in Graph</param>
|
||||
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
|
||||
act_path.append(&node, distance_to_node);
|
||||
|
||||
|
||||
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
|
||||
|
||||
VoronoiGraph::ExPath::SideBranches side_branches;
|
||||
|
||||
// skip node on circle when circle start at this 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
|
||||
std::vector<size_t> circle_indexes; // connected circles
|
||||
|
||||
std::vector<size_t> end_circle_indexes;
|
||||
// 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;
|
||||
|
||||
// detection of circle
|
||||
@ -2551,6 +2551,7 @@ VoronoiGraph::ExPath create_longest_branch_from_node(
|
||||
return result;
|
||||
}
|
||||
|
||||
// create result longest path from longest side branch
|
||||
VoronoiGraph::Path longest_path(std::move(side_branches.top()));
|
||||
side_branches.pop();
|
||||
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>
|
||||
/// PRIVATE:
|
||||
/// Extract the longest path from voronoi graph
|
||||
@ -2632,11 +2901,33 @@ void reshape_longest_path(VoronoiGraph::ExPath& path) {
|
||||
/// </summary>
|
||||
/// <param name="start_path">IN/OUT path in Graph</param>
|
||||
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);
|
||||
|
||||
|
||||
|
||||
reshape_longest_path(longest_path);
|
||||
// after reshape it shoud be longest path for whole Voronoi Graph
|
||||
return longest_path;
|
||||
@ -2985,7 +3276,7 @@ TEST_CASE("Sample small islands", "[VoronoiSkeleton]")
|
||||
{4 * 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_CW = r_CCW - size / 6;
|
||||
// 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;
|
||||
|
||||
ExPolygons islands = {
|
||||
triangle
|
||||
, square
|
||||
, sharp_triangle
|
||||
, rect
|
||||
, rect_with_hole
|
||||
, triangle_with_hole
|
||||
, rect_with_4_hole
|
||||
, mountains
|
||||
, double_circle
|
||||
double_circle
|
||||
// triangle
|
||||
//, square
|
||||
//, sharp_triangle
|
||||
//, rect
|
||||
//, rect_with_hole
|
||||
//, triangle_with_hole
|
||||
//, rect_with_4_hole
|
||||
//, mountains
|
||||
//, double_circle
|
||||
//, frog_leg
|
||||
};
|
||||
for (auto &island : islands) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user