diff --git a/tests/libslic3r/test_voronoi.cpp b/tests/libslic3r/test_voronoi.cpp index 9b7e86e8ea..34b84de857 100644 --- a/tests/libslic3r/test_voronoi.cpp +++ b/tests/libslic3r/test_voronoi.cpp @@ -1929,7 +1929,8 @@ struct VoronoiGraph struct Node; using Nodes = std::vector; struct Path; - struct Circle; + struct ExPath; + using Circle = Path; std::map data; }; @@ -1997,7 +1998,7 @@ struct VoronoiGraph::Node::Neighbor // pointer on graph node structure Node *graph_node; - // full fill constructor +public: Neighbor(const VD::edge_type *edge, double edge_length, double longest_distance, @@ -2009,36 +2010,53 @@ struct VoronoiGraph::Node::Neighbor {} }; -struct VoronoiGraph::Circle +/// +/// Data object represents path over nodes of VoronoiGraph +/// store their length of path +/// +struct VoronoiGraph::Path { - VoronoiGraph::Nodes path; - double length; - Circle(VoronoiGraph::Nodes path, double length) - : path(std::move(path)), length(length) + // row of neighbor Nodes + VoronoiGraph::Nodes path; // TODO: rename to nodes + + // length of path + // when circle contain length from back to front; + double length; +public: + Path() : path(), length(0.) {} + Path(VoronoiGraph::Nodes nodes, double length) + : path(std::move(nodes)), length(length) {} }; -struct VoronoiGraph::Path +/// +/// Data object +/// extends path with circles and side branches +/// +struct VoronoiGraph::ExPath : public VoronoiGraph::Path { - // row of Node created path with length - VoronoiGraph::Nodes path; // not main path is stored in secondary paths // key is pointer to source node using SideBranches = std::map>; SideBranches side_branches; - + // circles std::vector circles; - //VoronoiGraph::Nodes circles; - double length; - - // initial constructor - Path(VoronoiGraph::Nodes path, double length) - : path(std::move(path)), length(length), side_branches() - {} +public: + ExPath() = default; + /// + /// Append new node to path + /// + /// node to be append + /// distance of new neighbor + ExPath(const VoronoiGraph::Node *neighbor_node, + double neighbor_distance) + { + this->path.push_back(neighbor_node); + this->length += neighbor_distance; + } }; - /// /// calculate distances to border of island and length on skeleton /// @@ -2154,19 +2172,19 @@ double get_neighbor_distance(const VoronoiGraph::Node *from, /// Longest nodes path and its length std::pair find_longest_path_on_circle( VoronoiGraph::Circle & circle, - const VoronoiGraph::Path::SideBranches &side_branches, + const VoronoiGraph::ExPath::SideBranches &side_branches, const VoronoiGraph::Path & start_path) { - // !! this FIX circle lenght because at detection of circle it will cost time + // !! this FIX circle lenght because at detection of circle it will cost time to calculate it circle.length -= start_path.length; double half_circle_length = circle.length / 2.; double distance_on_circle = 0; - double max_length = 0; - bool is_longest_revers_direction = false; - const VoronoiGraph::Node *longest_circle_node = nullptr; - const VoronoiGraph::Path *longest_circle_branch = nullptr; + bool is_longest_revers_direction = false; + const VoronoiGraph::Node *longest_circle_node = nullptr; + const VoronoiGraph::Path *longest_circle_branch = nullptr; + double longest_branch_length = 0; bool is_short_revers_direction = false; // find longest side branch @@ -2179,19 +2197,19 @@ std::pair find_longest_path_on_circle( auto side_branches_item = side_branches.find(circle_node); if (side_branches_item != side_branches.end()) { // side_branches should be sorted by length - const auto &longest_side_branch = side_branches_item->second.front(); if (distance_on_circle > half_circle_length) is_short_revers_direction = true; - double node_max_length = start_path.length + - longest_side_branch.length + - ((is_short_revers_direction) ? - (circle.length - distance_on_circle) : - distance_on_circle); - if (max_length < node_max_length) { - max_length = node_max_length; + const auto &longest_node_branch = side_branches_item->second.front(); + double circle_branch_length = longest_node_branch.length + + ((is_short_revers_direction) ? + (circle.length - + distance_on_circle) : + distance_on_circle); + if (longest_branch_length < circle_branch_length) { + longest_branch_length = circle_branch_length; is_longest_revers_direction = is_short_revers_direction; longest_circle_node = circle_node; - longest_circle_branch = &longest_side_branch; + longest_circle_branch = &longest_node_branch; } } } @@ -2215,17 +2233,11 @@ std::pair find_longest_path_on_circle( circle.path.begin() + 1, circle_iterator+1); } - // path befor circle - VoronoiGraph::Nodes path = start_path.path; - // append part of circle - path.insert(path.end(), - circle_path.begin(), - circle_path.end()); - // append side branch - path.insert(path.end(), + // append longest side branch + circle_path.insert(circle_path.end(), longest_circle_branch->path.begin(), longest_circle_branch->path.end()); - return {path ,max_length}; + return {circle_path, longest_branch_length}; } @@ -2233,32 +2245,20 @@ void sort_path_by_length(std::vector &paths) { if (paths.size() <= 1) return; std::sort(paths.begin(), paths.end(), - [](const VoronoiGraph::Path &p1, const VoronoiGraph::Path &p2) { - return p1.length > p2.length; + [](const VoronoiGraph::Path &path1, const VoronoiGraph::Path &path2) { + return path1.length > path2.length; }); } -void create_longest_path_from_point(VoronoiGraph::Path &start_path); -void create_longest_path_from_neighbor( - VoronoiGraph::Path & next_path, - const VoronoiGraph::Node::Neighbor &neighbor) -{ - next_path.length += neighbor.edge_length; - const VoronoiGraph::Node *next_node = neighbor.graph_node; - next_path.path.push_back(next_node); - // stop when it is leaf - if (next_node->neighbors.size() == 1) return; - return create_longest_path_from_point(next_path); -} - /// /// function for detection circle in passed path /// /// IN/OUT: If exist append circle to path /// neighbor possible created circle /// TRUE when circle was created otherwise FALSE -bool create_circle(VoronoiGraph::Path & path, - const VoronoiGraph::Node::Neighbor &neighbor) +std::optional create_circle( + const VoronoiGraph::Path &path, + const VoronoiGraph::Node::Neighbor &neighbor) { VoronoiGraph::Nodes passed_nodes = path.path; // detection of circle @@ -2266,17 +2266,14 @@ bool create_circle(VoronoiGraph::Path & path, auto end_find = passed_nodes.end() - 1; const auto &path_item = std::find(passed_nodes.begin(), end_find, neighbor.graph_node); - if (path_item != end_find) { // circle detected - // separate Circle: - VoronoiGraph::Nodes circle_path(path_item, passed_nodes.end()); - // !!! Real circle lenght is calculated at fix method, - // circle_length contain lenght into start node of circle - double circle_length = path.length + neighbor.edge_length; - // solve of branch length will be at begin of cirlce - path.circles.emplace_back(std::move(circle_path), circle_length); - return true; - } - return false; + if (path_item == end_find) return {}; // circle not detected + // separate Circle: + VoronoiGraph::Nodes circle_path(path_item, passed_nodes.end()); + // !!! Real circle lenght is calculated in function find_longest_path_on_circle + // now circle_length contain also lenght of path before circle + double circle_length = path.length + neighbor.edge_length; + // solve of branch length will be at begin of cirlce + return VoronoiGraph::Circle(std::move(circle_path), circle_length); }; /// @@ -2287,43 +2284,59 @@ bool create_circle(VoronoiGraph::Path & path, /// !! need restructuralization by branch from start path /// /// IN/OUT path in Graph -void create_longest_path_from_point(VoronoiGraph::Path& start_path) +VoronoiGraph::ExPath create_longest_branch_from_node( + const VoronoiGraph::Node &node, + double distance_to_node = 0., + const VoronoiGraph::Path &prev_path = VoronoiGraph::Path({},0.)) { - const VoronoiGraph::Nodes &path = start_path.path; - size_t path_size = path.size(); - assert(path_size >= 1); + // append actual node + VoronoiGraph::Path act_path = prev_path; // make copy + act_path.path.push_back(&node); + act_path.length += distance_to_node; + + const VoronoiGraph::Nodes &prev_nodes = prev_path.path; + const VoronoiGraph::Node *prev_node = + (prev_nodes.size() >= 1) ? prev_nodes.back() : nullptr; - const VoronoiGraph::Node &node = *path.back(); const std::vector &neighbors = node.neighbors; size_t neighbor_count = neighbors.size(); - const VoronoiGraph::Node *prev_node = - (path_size >= 2) ? path[path_size - 2] : nullptr; - - std::vector paths; - paths.reserve(neighbor_count - 1); // one neighbor is prev node + std::vector side_branches; + side_branches.reserve(neighbor_count - 1); // one neighbor is prev node // when search on cirle store only branches not search for longest Path bool is_node_on_circle = false; + // skip node on circle when circle start at this node // vector --> multiple cirle could start at same node // prev node should be skiped to VoronoiGraph::Nodes skip_nodes({prev_node}); // circle + VoronoiGraph::ExPath result(&node, distance_to_node); // Depth search for path of all neighbors --> fill paths for (const VoronoiGraph::Node::Neighbor &neighbor : neighbors) { if (std::find(skip_nodes.begin(), skip_nodes.end(), neighbor.graph_node) != skip_nodes.end()) continue; // detection of circle - if (create_circle(start_path, neighbor)) { + auto circle_opt = create_circle(act_path, neighbor); + if (circle_opt.has_value()) { + result.circles.emplace_back(std::move(circle_opt.value())); is_node_on_circle = true; continue; } // create copy of path(not circles, not side_branches) - VoronoiGraph::Path next_path(start_path.path, start_path.length); - create_longest_path_from_neighbor(next_path, neighbor); + const VoronoiGraph::Node &next_node = *neighbor.graph_node; + // is next node leaf ? + if (next_node.neighbors.size() == 1) { + VoronoiGraph::Path side_branch({&next_node}, neighbor.edge_length); + side_branches.emplace_back(std::move(side_branch)); + continue; + } + VoronoiGraph::ExPath next_path = + create_longest_branch_from_node(next_node, neighbor.edge_length, act_path); + // over all new circles -> check if this node is on circle bool is_circle_neighbor = false; for (VoronoiGraph::Circle &circle : next_path.circles) { @@ -2333,7 +2346,7 @@ void create_longest_path_from_point(VoronoiGraph::Path& start_path) if (circle_item == circle.path.begin()) { // find longest path over circle and store it into next_path std::tie(next_path.path, next_path.length) = - find_longest_path_on_circle(circle, next_path.side_branches, start_path); + find_longest_path_on_circle(circle, next_path.side_branches, act_path); // skip twice checking of circle skip_nodes.push_back(circle.path.back()); } else { @@ -2343,52 +2356,47 @@ void create_longest_path_from_point(VoronoiGraph::Path& start_path) } } + // TODO: move insted of copy // copy side branches - start_path.side_branches.insert( - next_path.side_branches.begin(), - next_path.side_branches.end()); + if (!next_path.side_branches.empty()) + result.side_branches.insert( + next_path.side_branches.begin(), + next_path.side_branches.end()); + + // TODO: move insted of copy // copy circles - start_path.circles.insert( - start_path.circles.end(), - next_path.circles.begin(), - next_path.circles.end()); + if (!next_path.circles.empty()) + result.circles.insert( + result.circles.end(), + next_path.circles.begin(), + next_path.circles.end()); if (!is_circle_neighbor) - paths.push_back(next_path); + side_branches.emplace_back(std::move(next_path)); } // nothing to store // simple node on circle --> only input and output neighbor - if (paths.empty()) return; + if (side_branches.empty()) return result; // from longest path - sort_path_by_length(paths); - - // cut off start_path from branch - auto prepare_branches = [](std::vector &paths, - const VoronoiGraph::Path & start_path) { - for (VoronoiGraph::Path &path : paths) { - path.length -= start_path.length; - path.path.erase(path.path.begin(), - path.path.begin() + start_path.path.size()); - } - }; + sort_path_by_length(side_branches); if (is_node_on_circle) { // not search for longest path, it will eval on end of circle - prepare_branches(paths, start_path); - start_path.side_branches[&node] = paths; - return; + result.side_branches[&node] = side_branches; + return result; } - VoronoiGraph::Path longest_path(std::move(paths.front())); - paths.erase(paths.begin()); - if (!paths.empty()) { - prepare_branches(paths, start_path); - start_path.side_branches[&node] = paths; + VoronoiGraph::Path longest_path(std::move(side_branches.front())); + side_branches.erase(side_branches.begin()); + if (!side_branches.empty()) { + result.side_branches[&node] = side_branches; } - start_path.path = std::move(longest_path.path); - start_path.length = longest_path.length; + longest_path.path.insert(longest_path.path.begin(), &node); + result.path = std::move(longest_path.path); + result.length += longest_path.length; + return result; } // only for debug purpose double get_length(VoronoiGraph::Nodes &nodes) { @@ -2409,7 +2417,7 @@ double get_length(VoronoiGraph::Nodes &nodes) { /// When exist longer path swap branches /// /// IN/OUT path to be fixed after creating longest path from one point -void reshape_longest_path(VoronoiGraph::Path& path) { +void reshape_longest_path(VoronoiGraph::ExPath& path) { assert(path.path.size() >= 1); double actual_length = 0.; @@ -2462,12 +2470,12 @@ void reshape_longest_path(VoronoiGraph::Path& path) { /// Restructuralize path by branch created from random point /// /// IN/OUT path in Graph -VoronoiGraph::Path create_longest_path(const VoronoiGraph::Node *start_node) +VoronoiGraph::ExPath create_longest_path(const VoronoiGraph::Node *start_node) { - VoronoiGraph::Path path({start_node}, 0.); - create_longest_path_from_point(path); - reshape_longest_path(path); - return path; + 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; } /// @@ -2557,7 +2565,7 @@ Point get_center_of_path(const VoronoiGraph::Nodes& path, double path_length) /// Vector of sampled points or Empty when distance from edge is bigger than max_distance std::vector sample_voronoi_graph(const VoronoiGraph &graph, const SampleConfig &config, - VoronoiGraph::Path &longest_path) + VoronoiGraph::ExPath &longest_path) { // first vertex on contour: const VoronoiGraph::Node *start_node = nullptr; @@ -2599,7 +2607,7 @@ void draw(SVG &svg, const VoronoiGraph &graph, coord_t width) Point center = from + to; center *= .5; - svg.draw_text(center, (std::to_string(std::round(n.edge_length/3e5)/100.)).c_str(), "gray"); + //svg.draw_text(center, (std::to_string(std::round(n.edge_length/3e5)/100.)).c_str(), "gray"); } } } @@ -2617,13 +2625,13 @@ void draw(SVG &svg, const VoronoiGraph::Nodes& path, coord_t width, const char* Point to(node->vertex->x(), node->vertex->y()); svg.draw(Line(from, to), color, width); - svg.draw_text(from, std::to_string(index - 1).c_str(), color); - svg.draw_text(to, std::to_string(index).c_str(), color); + //svg.draw_text(from, std::to_string(index - 1).c_str(), color); + //svg.draw_text(to, std::to_string(index).c_str(), color); prev_node = node; } } -void draw(SVG &svg, const VoronoiGraph::Path &path, coord_t width) { +void draw(SVG &svg, const VoronoiGraph::ExPath &path, coord_t width) { const char *circlePathColor = "green"; const char *sideBranchesColor = "blue"; const char *mainPathColor = "red"; @@ -2642,17 +2650,17 @@ void draw(SVG &svg, const VoronoiGraph::Path &path, coord_t width) { } std::vector sample_in_center(const ExPolygon & island, - const SampleConfig &config) + const SampleConfig &config, bool visualize = true) { VD vd; Lines lines = to_lines(island); construct_voronoi(lines.begin(), lines.end(), &vd); Slic3r::Voronoi::annotate_inside_outside(vd, lines); VoronoiGraph skeleton = getSkeleton(vd, lines); - VoronoiGraph::Path longest_path({},0); + VoronoiGraph::ExPath longest_path; std::vector samples = sample_voronoi_graph(skeleton, config, longest_path); - { // visualization + if(visualize){ // visualization static int counter = 0; BoundingBox bb; double scale = bb.size().x(); @@ -2682,10 +2690,30 @@ std::vector test_island_sampling(const ExPolygon & island, return points; } +TEST_CASE("Sample speed test on FrogLegs", "[VoronoiSkeleton]") { + TriangleMesh mesh = load_model("frog_legs.obj"); + TriangleMeshSlicer slicer{&mesh}; + std::vector grid({0.1f}); + std::vector slices; + slicer.slice(grid, SlicingMode::Regular, 0.05f, &slices, [] {}); + ExPolygon frog_leg = slices.front()[1]; + + double size = 3e7; + SampleConfig cfg; + cfg.max_distance = size + 0.1; + cfg.sample_size = size / 5; + cfg.start_distance = 0.2 * size; // radius of support head + cfg.curve_sample = 0.1 * size; + cfg.max_length_for_one_support_point = 3 * size; + + for (int i = 0; i < 1; ++i) { + auto points = sample_in_center(frog_leg, cfg, false); + } +} + TEST_CASE("Sample small islands", "[VoronoiSkeleton]") { double size = 3e7; - int count = 5; ExPolygon triangle( Polygon{{.0, .0}, {size, .0}, @@ -2741,27 +2769,24 @@ TEST_CASE("Sample small islands", "[VoronoiSkeleton]") {4 * size5, size5}, {3 * size5, size5}}}; + + //* TriangleMesh mesh = load_model("frog_legs.obj"); TriangleMeshSlicer slicer{&mesh}; - - - std::vector grid({ - 0.1f - //static_cast(mesh.bounding_box().center().z()) - }); + std::vector grid({ 0.1f }); std::vector slices; slicer.slice(grid, SlicingMode::Regular, 0.05f, &slices, [] {}); - ExPolygon frog_leg = slices.front()[1]; + ExPolygon frog_leg = slices.front()[1]; //*/ SampleConfig cfg; cfg.max_distance = size + 0.1; - cfg.sample_size = size / count; + cfg.sample_size = size / 5; cfg.start_distance = 0.2*size; // radius of support head cfg.curve_sample = 0.1 *size; cfg.max_length_for_one_support_point = 3 * size; - ExPolygons islands = { - triangle + ExPolygons islands = { frog_leg, + triangle , square , sharp_triangle , rect