TreeNode.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. //Copyright (c) 2021 Ultimaker B.V.
  2. //CuraEngine is released under the terms of the AGPLv3 or higher.
  3. #include "TreeNode.hpp"
  4. #include "../../Geometry.hpp"
  5. namespace Slic3r::FillLightning {
  6. coord_t Node::getWeightedDistance(const Point& unsupported_location, const coord_t& supporting_radius) const
  7. {
  8. constexpr coord_t min_valence_for_boost = 0;
  9. constexpr coord_t max_valence_for_boost = 4;
  10. constexpr coord_t valence_boost_multiplier = 4;
  11. const size_t valence = (!m_is_root) + m_children.size();
  12. const coord_t valence_boost = (min_valence_for_boost < valence && valence < max_valence_for_boost) ? valence_boost_multiplier * supporting_radius : 0;
  13. const auto dist_here = coord_t((getLocation() - unsupported_location).cast<double>().norm());
  14. return dist_here - valence_boost;
  15. }
  16. bool Node::hasOffspring(const NodeSPtr& to_be_checked) const
  17. {
  18. if (to_be_checked == shared_from_this())
  19. return true;
  20. for (auto& child_ptr : m_children)
  21. if (child_ptr->hasOffspring(to_be_checked))
  22. return true;
  23. return false;
  24. }
  25. NodeSPtr Node::addChild(const Point& child_loc)
  26. {
  27. assert(m_p != child_loc);
  28. NodeSPtr child = Node::create(child_loc);
  29. return addChild(child);
  30. }
  31. NodeSPtr Node::addChild(NodeSPtr& new_child)
  32. {
  33. assert(new_child != shared_from_this());
  34. //assert(p != new_child->p); // NOTE: No problem for now. Issue to solve later. Maybe even afetr final. Low prio.
  35. m_children.push_back(new_child);
  36. new_child->m_parent = shared_from_this();
  37. new_child->m_is_root = false;
  38. return new_child;
  39. }
  40. void Node::propagateToNextLayer(
  41. std::vector<NodeSPtr>& next_trees,
  42. const Polygons& next_outlines,
  43. const EdgeGrid::Grid& outline_locator,
  44. const coord_t prune_distance,
  45. const coord_t smooth_magnitude,
  46. const coord_t max_remove_colinear_dist) const
  47. {
  48. auto tree_below = deepCopy();
  49. tree_below->prune(prune_distance);
  50. tree_below->straighten(smooth_magnitude, max_remove_colinear_dist);
  51. if (tree_below->realign(next_outlines, outline_locator, next_trees))
  52. next_trees.push_back(tree_below);
  53. }
  54. // NOTE: Depth-first, as currently implemented.
  55. // Skips the root (because that has no root itself), but all initial nodes will have the root point anyway.
  56. void Node::visitBranches(const std::function<void(const Point&, const Point&)>& visitor) const
  57. {
  58. for (const auto& node : m_children) {
  59. assert(node->m_parent.lock() == shared_from_this());
  60. visitor(m_p, node->m_p);
  61. node->visitBranches(visitor);
  62. }
  63. }
  64. // NOTE: Depth-first, as currently implemented.
  65. void Node::visitNodes(const std::function<void(NodeSPtr)>& visitor)
  66. {
  67. visitor(shared_from_this());
  68. for (const auto& node : m_children) {
  69. assert(node->m_parent.lock() == shared_from_this());
  70. node->visitNodes(visitor);
  71. }
  72. }
  73. Node::Node(const Point& p, const std::optional<Point>& last_grounding_location /*= std::nullopt*/) :
  74. m_is_root(true), m_p(p), m_last_grounding_location(last_grounding_location)
  75. {}
  76. NodeSPtr Node::deepCopy() const
  77. {
  78. NodeSPtr local_root = Node::create(m_p);
  79. local_root->m_is_root = m_is_root;
  80. if (m_is_root)
  81. {
  82. local_root->m_last_grounding_location = m_last_grounding_location.value_or(m_p);
  83. }
  84. local_root->m_children.reserve(m_children.size());
  85. for (const auto& node : m_children)
  86. {
  87. NodeSPtr child = node->deepCopy();
  88. child->m_parent = local_root;
  89. local_root->m_children.push_back(child);
  90. }
  91. return local_root;
  92. }
  93. void Node::reroot(const NodeSPtr &new_parent)
  94. {
  95. if (! m_is_root) {
  96. auto old_parent = m_parent.lock();
  97. old_parent->reroot(shared_from_this());
  98. m_children.push_back(old_parent);
  99. }
  100. if (new_parent) {
  101. m_children.erase(std::remove(m_children.begin(), m_children.end(), new_parent), m_children.end());
  102. m_is_root = false;
  103. m_parent = new_parent;
  104. } else {
  105. m_is_root = true;
  106. m_parent.reset();
  107. }
  108. }
  109. NodeSPtr Node::closestNode(const Point& loc)
  110. {
  111. NodeSPtr result = shared_from_this();
  112. auto closest_dist2 = coord_t((m_p - loc).cast<double>().norm());
  113. for (const auto& child : m_children) {
  114. NodeSPtr candidate_node = child->closestNode(loc);
  115. const auto child_dist2 = coord_t((candidate_node->m_p - loc).cast<double>().norm());
  116. if (child_dist2 < closest_dist2) {
  117. closest_dist2 = child_dist2;
  118. result = candidate_node;
  119. }
  120. }
  121. return result;
  122. }
  123. bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, const coord_t within_max_dist)
  124. {
  125. struct Visitor {
  126. bool operator()(coord_t iy, coord_t ix) {
  127. // Called with a row and colum of the grid cell, which is intersected by a line.
  128. auto cell_data_range = grid.cell_data_range(iy, ix);
  129. for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) {
  130. // End points of the line segment and their vector.
  131. auto segment = grid.segment(*it_contour_and_segment);
  132. if (Vec2d ip; Geometry::segment_segment_intersection(segment.first.cast<double>(), segment.second.cast<double>(), this->line_a, this->line_b, ip))
  133. if (double d = (this->intersection_pt - this->line_b).squaredNorm(); d < d2min) {
  134. this->d2min = d;
  135. this->intersection_pt = ip;
  136. }
  137. }
  138. // Continue traversing the grid along the edge.
  139. return true;
  140. }
  141. const EdgeGrid::Grid& grid;
  142. Vec2d line_a;
  143. Vec2d line_b;
  144. Vec2d intersection_pt;
  145. double d2min { std::numeric_limits<double>::max() };
  146. } visitor { outline_locator, a.cast<double>(), b.cast<double>() };
  147. outline_locator.visit_cells_intersecting_line(a, b, visitor);
  148. if (visitor.d2min < double(within_max_dist) * double(within_max_dist)) {
  149. result = Point(visitor.intersection_pt);
  150. return true;
  151. }
  152. return false;
  153. }
  154. bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector<NodeSPtr>& rerooted_parts)
  155. {
  156. if (outlines.empty())
  157. return false;
  158. if (contains(outlines, m_p)) {
  159. // Only keep children that have an unbroken connection to here, realign will put the rest in rerooted parts due to recursion:
  160. Point coll;
  161. bool reground_me = false;
  162. m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&](const NodeSPtr &child) {
  163. bool connect_branch = child->realign(outlines, outline_locator, rerooted_parts);
  164. // Find an intersection of the line segment from p to child->p, at maximum outline_locator.resolution() * 2 distance from p.
  165. if (connect_branch && lineSegmentPolygonsIntersection(child->m_p, m_p, outline_locator, coll, outline_locator.resolution() * 2)) {
  166. child->m_last_grounding_location.reset();
  167. child->m_parent.reset();
  168. child->m_is_root = true;
  169. rerooted_parts.push_back(child);
  170. reground_me = true;
  171. connect_branch = false;
  172. }
  173. return ! connect_branch;
  174. }), m_children.end());
  175. if (reground_me)
  176. m_last_grounding_location.reset();
  177. return true;
  178. }
  179. // 'Lift' any decendants out of this tree:
  180. for (auto& child : m_children)
  181. if (child->realign(outlines, outline_locator, rerooted_parts)) {
  182. child->m_last_grounding_location = m_p;
  183. child->m_parent.reset();
  184. child->m_is_root = true;
  185. rerooted_parts.push_back(child);
  186. }
  187. m_children.clear();
  188. return false;
  189. }
  190. void Node::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist)
  191. {
  192. straighten(magnitude, m_p, 0, int64_t(max_remove_colinear_dist) * int64_t(max_remove_colinear_dist));
  193. }
  194. Node::RectilinearJunction Node::straighten(
  195. const coord_t magnitude,
  196. const Point& junction_above,
  197. const coord_t accumulated_dist,
  198. const int64_t max_remove_colinear_dist2)
  199. {
  200. constexpr coord_t junction_magnitude_factor_numerator = 3;
  201. constexpr coord_t junction_magnitude_factor_denominator = 4;
  202. const coord_t junction_magnitude = magnitude * junction_magnitude_factor_numerator / junction_magnitude_factor_denominator;
  203. if (m_children.size() == 1)
  204. {
  205. auto child_p = m_children.front();
  206. auto child_dist = coord_t((m_p - child_p->m_p).cast<double>().norm());
  207. RectilinearJunction junction_below = child_p->straighten(magnitude, junction_above, accumulated_dist + child_dist, max_remove_colinear_dist2);
  208. coord_t total_dist_to_junction_below = junction_below.total_recti_dist;
  209. const Point& a = junction_above;
  210. Point b = junction_below.junction_loc;
  211. if (a != b) // should always be true!
  212. {
  213. Point ab = b - a;
  214. Point destination = (a.cast<int64_t>() + ab.cast<int64_t>() * int64_t(accumulated_dist) / std::max(int64_t(1), int64_t(total_dist_to_junction_below))).cast<coord_t>();
  215. if ((destination - m_p).cast<int64_t>().squaredNorm() <= int64_t(magnitude) * int64_t(magnitude))
  216. m_p = destination;
  217. else
  218. m_p += ((destination - m_p).cast<double>().normalized() * magnitude).cast<coord_t>();
  219. }
  220. { // remove nodes on linear segments
  221. constexpr coord_t close_enough = 10;
  222. child_p = m_children.front(); //recursive call to straighten might have removed the child
  223. const NodeSPtr& parent_node = m_parent.lock();
  224. if (parent_node &&
  225. (child_p->m_p - parent_node->m_p).cast<int64_t>().squaredNorm() < max_remove_colinear_dist2 &&
  226. Line::distance_to_squared(m_p, parent_node->m_p, child_p->m_p) < close_enough * close_enough) {
  227. child_p->m_parent = m_parent;
  228. for (auto& sibling : parent_node->m_children)
  229. { // find this node among siblings
  230. if (sibling == shared_from_this())
  231. {
  232. sibling = child_p; // replace this node by child
  233. break;
  234. }
  235. }
  236. }
  237. }
  238. return junction_below;
  239. }
  240. else
  241. {
  242. constexpr coord_t weight = 1000;
  243. Point junction_moving_dir = ((junction_above - m_p).cast<double>().normalized() * weight).cast<coord_t>();
  244. bool prevent_junction_moving = false;
  245. for (auto& child_p : m_children)
  246. {
  247. const auto child_dist = coord_t((m_p - child_p->m_p).cast<double>().norm());
  248. RectilinearJunction below = child_p->straighten(magnitude, m_p, child_dist, max_remove_colinear_dist2);
  249. junction_moving_dir += ((below.junction_loc - m_p).cast<double>().normalized() * weight).cast<coord_t>();
  250. if (below.total_recti_dist < magnitude) // TODO: make configurable?
  251. {
  252. prevent_junction_moving = true; // prevent flipflopping in branches due to straightening and junctoin moving clashing
  253. }
  254. }
  255. if (junction_moving_dir != Point(0, 0) && ! m_children.empty() && ! m_is_root && ! prevent_junction_moving)
  256. {
  257. auto junction_moving_dir_len = coord_t(junction_moving_dir.norm());
  258. if (junction_moving_dir_len > junction_magnitude)
  259. {
  260. junction_moving_dir = junction_moving_dir * ((double)junction_magnitude / (double)junction_moving_dir_len);
  261. }
  262. m_p += junction_moving_dir;
  263. }
  264. return RectilinearJunction{ accumulated_dist, m_p };
  265. }
  266. }
  267. // Prune the tree from the extremeties (leaf-nodes) until the pruning distance is reached.
  268. coord_t Node::prune(const coord_t& pruning_distance)
  269. {
  270. if (pruning_distance <= 0)
  271. return 0;
  272. coord_t max_distance_pruned = 0;
  273. for (auto child_it = m_children.begin(); child_it != m_children.end(); ) {
  274. auto& child = *child_it;
  275. coord_t dist_pruned_child = child->prune(pruning_distance);
  276. if (dist_pruned_child >= pruning_distance)
  277. { // pruning is finished for child; dont modify further
  278. max_distance_pruned = std::max(max_distance_pruned, dist_pruned_child);
  279. ++child_it;
  280. } else {
  281. const Point a = getLocation();
  282. const Point b = child->getLocation();
  283. const Point ba = a - b;
  284. const auto ab_len = coord_t(ba.cast<double>().norm());
  285. if (dist_pruned_child + ab_len <= pruning_distance) {
  286. // we're still in the process of pruning
  287. assert(child->m_children.empty() && "when pruning away a node all it's children must already have been pruned away");
  288. max_distance_pruned = std::max(max_distance_pruned, dist_pruned_child + ab_len);
  289. child_it = m_children.erase(child_it);
  290. } else {
  291. // pruning stops in between this node and the child
  292. const Point n = b + (ba.cast<double>().normalized() * (pruning_distance - dist_pruned_child)).cast<coord_t>();
  293. assert(std::abs((n - b).cast<double>().norm() + dist_pruned_child - pruning_distance) < 10 && "total pruned distance must be equal to the pruning_distance");
  294. max_distance_pruned = std::max(max_distance_pruned, pruning_distance);
  295. child->setLocation(n);
  296. ++child_it;
  297. }
  298. }
  299. }
  300. return max_distance_pruned;
  301. }
  302. void Node::convertToPolylines(Polylines &output, const coord_t line_overlap) const
  303. {
  304. Polylines result;
  305. result.emplace_back();
  306. convertToPolylines(0, result);
  307. removeJunctionOverlap(result, line_overlap);
  308. append(output, std::move(result));
  309. }
  310. void Node::convertToPolylines(size_t long_line_idx, Polylines &output) const
  311. {
  312. if (m_children.empty()) {
  313. output[long_line_idx].points.push_back(m_p);
  314. return;
  315. }
  316. size_t first_child_idx = rand() % m_children.size();
  317. m_children[first_child_idx]->convertToPolylines(long_line_idx, output);
  318. output[long_line_idx].points.push_back(m_p);
  319. for (size_t idx_offset = 1; idx_offset < m_children.size(); idx_offset++) {
  320. size_t child_idx = (first_child_idx + idx_offset) % m_children.size();
  321. const Node& child = *m_children[child_idx];
  322. output.emplace_back();
  323. size_t child_line_idx = output.size() - 1;
  324. child.convertToPolylines(child_line_idx, output);
  325. output[child_line_idx].points.emplace_back(m_p);
  326. }
  327. }
  328. void Node::removeJunctionOverlap(Polylines &result_lines, const coord_t line_overlap) const
  329. {
  330. const coord_t reduction = line_overlap;
  331. size_t res_line_idx = 0;
  332. while (res_line_idx < result_lines.size()) {
  333. Polyline &polyline = result_lines[res_line_idx];
  334. if (polyline.size() <= 1) {
  335. polyline = std::move(result_lines.back());
  336. result_lines.pop_back();
  337. continue;
  338. }
  339. coord_t to_be_reduced = reduction;
  340. Point a = polyline.back();
  341. for (int point_idx = int(polyline.size()) - 2; point_idx >= 0; point_idx--) {
  342. const Point b = polyline.points[point_idx];
  343. const Point ab = b - a;
  344. const auto ab_len = coord_t(ab.cast<double>().norm());
  345. if (ab_len >= to_be_reduced) {
  346. polyline.points.back() = a + (ab.cast<double>() * (double(to_be_reduced) / ab_len)).cast<coord_t>();
  347. break;
  348. } else {
  349. to_be_reduced -= ab_len;
  350. polyline.points.pop_back();
  351. }
  352. a = b;
  353. }
  354. if (polyline.size() <= 1) {
  355. polyline = std::move(result_lines.back());
  356. result_lines.pop_back();
  357. } else
  358. ++ res_line_idx;
  359. }
  360. }
  361. #ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
  362. void export_to_svg(const NodeSPtr &root_node, SVG &svg)
  363. {
  364. for (const NodeSPtr &children : root_node->m_children) {
  365. svg.draw(Line(root_node->getLocation(), children->getLocation()), "red");
  366. export_to_svg(children, svg);
  367. }
  368. }
  369. void export_to_svg(const std::string &path, const Polygons &contour, const std::vector<NodeSPtr> &root_nodes) {
  370. BoundingBox bbox = get_extents(contour);
  371. bbox.offset(SCALED_EPSILON);
  372. SVG svg(path, bbox);
  373. svg.draw_outline(contour, "blue");
  374. for (const NodeSPtr &root_node: root_nodes) {
  375. for (const NodeSPtr &children: root_node->m_children) {
  376. svg.draw(Line(root_node->getLocation(), children->getLocation()), "red");
  377. export_to_svg(children, svg);
  378. }
  379. }
  380. }
  381. #endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */
  382. } // namespace Slic3r::FillLightning