diff --git a/tests/libslic3r/test_voronoi.cpp b/tests/libslic3r/test_voronoi.cpp index 7dabdb3e2e..44bbd1016a 100644 --- a/tests/libslic3r/test_voronoi.cpp +++ b/tests/libslic3r/test_voronoi.cpp @@ -2061,14 +2061,22 @@ struct VoronoiGraph::ExPath : public VoronoiGraph::Path using SideBranches = std::priority_queue< VoronoiGraph::Path, std::vector, OrderLengthFromLongest>; using SideBranchesMap = std::map; + + // 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 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> 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 /// /// IN/OUT path in Graph 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 &neighbors = node.neighbors; - size_t neighbor_count = neighbors.size(); - std::priority_queue, - 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 circle_indexes; // connected circles - std::vector 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) { } } +/// +/// DTO for process node to depth search to get longest path in voronoi graph +/// +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 skip_nodes; // circle + + // store all circle indexes this node is lay on + // used to create connected circles structure + std::vector 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 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>; + virtual ~IStackFunction() = default; + virtual void process(CallStack& call_stack) = 0; +}; + +class PostProcessNeighbors; + +/// +/// create on stack +/// 1 * PostProcessNeighbors +/// N * ExpandNode +/// +class EvaluateNeighbor : public IStackFunction +{ + std::unique_ptr 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); +}; + +/// +/// call after all neighbors are processed +/// +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; + } +}; + +/// +/// 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 +/// +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( + data); + VoronoiGraph::ExPath &neighbor_path = post_process_neighbor + ->neighbor_path; + + call_stack.emplace(std::move(post_process_neighbor)); + call_stack.emplace( + std::make_unique( + 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(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(data, neighbor)); +} + /// /// PRIVATE: /// Extract the longest path from voronoi graph @@ -2632,11 +2901,33 @@ void reshape_longest_path(VoronoiGraph::ExPath& path) { /// /// IN/OUT path in Graph VoronoiGraph::ExPath create_longest_path(const VoronoiGraph::Node *start_node) +{ + VoronoiGraph::ExPath longest_path; + IStackFunction::CallStack call_stack; + call_stack.emplace(std::make_unique(longest_path, start_node)); + + // depth search for longest path in graph + while (!call_stack.empty()) { + std::unique_ptr 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; +} + +/// +/// PRIVATE: +/// Extract the longest path from voronoi graph +/// Restructuralize path by branch created from random point +/// +/// IN/OUT path in Graph +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) {