1 //Copyright (c) 2018 Ultimaker B.V.
2 //CuraEngine is released under the terms of the AGPLv3 or higher.
3 
4 #include <list>
5 #include <sstream>
6 #include <unordered_set>
7 
8 #include "linearAlg2D.h"
9 #include "polygonUtils.h"
10 #include "SparsePointGridInclusive.h"
11 #include "../utils/logoutput.h"
12 
13 #ifdef DEBUG
14 #include "AABB.h"
15 #include "SVG.h"
16 #endif
17 
18 namespace cura
19 {
20 
__anonebed223a0102(Point)21 const std::function<int(Point)> PolygonUtils::no_penalty_function = [](Point){ return 0; };
22 
segmentLength(PolygonsPointIndex start,PolygonsPointIndex end)23 int64_t PolygonUtils::segmentLength(PolygonsPointIndex start, PolygonsPointIndex end)
24 {
25     assert(start.poly_idx == end.poly_idx);
26     int64_t segment_length = 0;
27     Point prev_vert = start.p();
28     ConstPolygonRef poly = (*start.polygons)[start.poly_idx];
29     for (unsigned int point_idx = 1; point_idx <= poly.size(); point_idx++)
30     {
31         unsigned int vert_idx = (start.point_idx + point_idx) % poly.size();
32         Point vert = poly[vert_idx];
33         segment_length += vSize(vert - prev_vert);
34 
35         if (vert_idx == end.point_idx)
36         { // break at the end of the loop, so that [end] and [start] may be the same
37             return segment_length;
38         }
39         prev_vert = vert;
40     }
41     assert(false && "The segment end should have been encountered!");
42     return segment_length;
43 }
44 
spreadDots(PolygonsPointIndex start,PolygonsPointIndex end,unsigned int n_dots,std::vector<ClosestPolygonPoint> & result)45 void PolygonUtils::spreadDots(PolygonsPointIndex start, PolygonsPointIndex end, unsigned int n_dots, std::vector<ClosestPolygonPoint>& result)
46 {
47     assert(start.poly_idx == end.poly_idx);
48     int64_t segment_length = segmentLength(start, end);
49 
50     ConstPolygonRef poly = (*start.polygons)[start.poly_idx];
51     unsigned int n_dots_in_between = n_dots;
52     if (start == end)
53     {
54         result.emplace_back(start.p(), start.point_idx, poly);
55         n_dots_in_between--; // generate one less below, because we already pushed a point to the result
56     }
57 
58     int64_t wipe_point_dist = segment_length / (n_dots_in_between + 1); // distance between two wipe points; keep a distance at both sides of the segment
59 
60     int64_t dist_past_vert_to_insert_point = wipe_point_dist;
61     unsigned int n_points_generated = 0;
62     PolygonsPointIndex vert = start;
63     while (true)
64     {
65         Point p0 = vert.p();
66         Point p1 = vert.next().p();
67         Point p0p1 = p1 - p0;
68         int64_t p0p1_length = vSize(p0p1);
69 
70         for ( ; dist_past_vert_to_insert_point < p0p1_length && n_points_generated < n_dots_in_between; dist_past_vert_to_insert_point += wipe_point_dist)
71         {
72             result.emplace_back(p0 + normal(p0p1, dist_past_vert_to_insert_point), vert.point_idx, poly);
73             n_points_generated++;
74         }
75         dist_past_vert_to_insert_point -= p0p1_length;
76 
77         ++vert;
78         if (vert == end)
79         { // break at end of loop to allow for [start] and [end] being the same, meaning the full polygon
80             break;
81         }
82     }
83     assert(result.size() == n_dots && "we didn't generate as many wipe locations as we asked for.");
84 }
85 
getVertexInwardNormal(ConstPolygonRef poly,unsigned int point_idx)86 Point PolygonUtils::getVertexInwardNormal(ConstPolygonRef poly, unsigned int point_idx)
87 {
88     Point p1 = poly[point_idx];
89 
90     int p0_idx;
91     for (p0_idx = int(point_idx) - 1; (unsigned int)p0_idx != point_idx; p0_idx = p0_idx - 1)
92     { // find the last point different from p1
93         if (p0_idx == -1)
94         {
95             p0_idx = poly.size() - 1;
96         }
97         if (poly[p0_idx] != p1)
98         {
99             break;
100         }
101     }
102     Point p0 = poly[p0_idx];
103 
104     unsigned int p2_idx;
105     for (p2_idx = point_idx + 1; p2_idx != point_idx; p2_idx = p2_idx + 1)
106     { // find the next point different from p1
107         if (p2_idx == poly.size())
108         {
109             p2_idx = 0;
110         }
111         if (poly[p2_idx] != p1)
112         {
113             break;
114         }
115     }
116     const Point& p2 = poly[p2_idx];
117 
118     Point off0 = turn90CCW(normal(p1 - p0, MM2INT(10.0))); // 10.0 for some precision
119     Point off1 = turn90CCW(normal(p2 - p1, MM2INT(10.0))); // 10.0 for some precision
120     Point n = off0 + off1;
121     return n;
122 }
123 
124 
getBoundaryPointWithOffset(ConstPolygonRef poly,unsigned int point_idx,int64_t offset)125 Point PolygonUtils::getBoundaryPointWithOffset(ConstPolygonRef poly, unsigned int point_idx, int64_t offset)
126 {
127     return poly[point_idx] + normal(getVertexInwardNormal(poly, point_idx), -offset);
128 }
129 
moveInsideDiagonally(ClosestPolygonPoint point_on_boundary,int64_t inset)130 Point PolygonUtils::moveInsideDiagonally(ClosestPolygonPoint point_on_boundary, int64_t inset)
131 {
132     if (!point_on_boundary.isValid())
133     {
134         return no_point;
135     }
136     ConstPolygonRef poly = **point_on_boundary.poly;
137     Point p0 = poly[point_on_boundary.point_idx];
138     Point p1 = poly[(point_on_boundary.point_idx + 1) % poly.size()];
139     if (vSize2(p0 - point_on_boundary.location) < vSize2(p1 - point_on_boundary.location))
140     {
141         return point_on_boundary.location + normal(getVertexInwardNormal(poly, point_on_boundary.point_idx), inset);
142     }
143     else
144     {
145         return point_on_boundary.location + normal(getVertexInwardNormal(poly, (point_on_boundary.point_idx + 1) % poly.size()), inset);
146     }
147 }
148 
149 
moveOutside(const Polygons & polygons,Point & from,int distance,int64_t maxDist2)150 unsigned int PolygonUtils::moveOutside(const Polygons& polygons, Point& from, int distance, int64_t maxDist2)
151 {
152     return moveInside(polygons, from, -distance, maxDist2);
153 }
154 
moveInside2(const Polygons & polygons,Point & from,const int distance,const int64_t max_dist2,const Polygons * loc_to_line_polygons,const LocToLineGrid * loc_to_line_grid,const std::function<int (Point)> & penalty_function)155 ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& polygons, Point& from, const int distance, const int64_t max_dist2, const Polygons* loc_to_line_polygons, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
156 {
157     std::optional<ClosestPolygonPoint> closest_polygon_point;
158     if (loc_to_line_grid)
159     {
160         closest_polygon_point = findClose(from, *loc_to_line_polygons, *loc_to_line_grid, penalty_function);
161     }
162     if (!closest_polygon_point)
163     {
164         closest_polygon_point = findClosest(from, polygons, penalty_function);
165     }
166     return _moveInside2(*closest_polygon_point, distance, from, max_dist2);
167 }
168 
moveInside2(const Polygons & loc_to_line_polygons,ConstPolygonRef polygon,Point & from,const int distance,const int64_t max_dist2,const LocToLineGrid * loc_to_line_grid,const std::function<int (Point)> & penalty_function)169 ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& loc_to_line_polygons, ConstPolygonRef polygon, Point& from, const int distance, const int64_t max_dist2, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
170 {
171     std::optional<ClosestPolygonPoint> closest_polygon_point;
172     if (loc_to_line_grid)
173     {
174         closest_polygon_point = findClose(from, loc_to_line_polygons, *loc_to_line_grid, penalty_function);
175     }
176     if (!closest_polygon_point)
177     {
178         closest_polygon_point = findClosest(from, polygon, penalty_function);
179     }
180     return _moveInside2(*closest_polygon_point, distance, from, max_dist2);
181 }
182 
_moveInside2(const ClosestPolygonPoint & closest_polygon_point,const int distance,Point & from,const int64_t max_dist2)183 ClosestPolygonPoint PolygonUtils::_moveInside2(const ClosestPolygonPoint& closest_polygon_point, const int distance, Point& from, const int64_t max_dist2)
184 {
185     if (!closest_polygon_point.isValid())
186     {
187         return ClosestPolygonPoint(); // stub with invalid indices to signify we haven't found any
188     }
189     const Point v_boundary_from = from - closest_polygon_point.location;
190     Point result = moveInside(closest_polygon_point, distance);
191     const Point v_boundary_result = result - closest_polygon_point.location;
192     if (dot(v_boundary_result, v_boundary_from) > 0)
193     { // point was already on the correct side of the polygon
194         if (vSize2(v_boundary_from) > distance * distance)
195         { // [from] was already on the correct side of the boudary by enough distance
196             // don't change [from]
197             return closest_polygon_point;
198         }
199         else
200         {
201             from = result;
202             return closest_polygon_point;
203         }
204     }
205     else
206     {
207         if (vSize2(v_boundary_from) > max_dist2)
208         {
209             return ClosestPolygonPoint(*closest_polygon_point.poly); // stub with invalid indices to signify we haven't found any
210         }
211         else
212         {
213             from = result;
214             return closest_polygon_point;
215         }
216     }
217 }
218 
219 
220 /*
221  * Implementation assumes moving inside, but moving outside should just as well be possible.
222  */
moveInside(const Polygons & polygons,Point & from,int distance,int64_t maxDist2)223 unsigned int PolygonUtils::moveInside(const Polygons& polygons, Point& from, int distance, int64_t maxDist2)
224 {
225     Point ret = from;
226     int64_t bestDist2 = std::numeric_limits<int64_t>::max();
227     unsigned int bestPoly = NO_INDEX;
228     bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
229     for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
230     {
231         ConstPolygonRef poly = polygons[poly_idx];
232         if (poly.size() < 2)
233             continue;
234         Point p0 = poly[poly.size()-2];
235         Point p1 = poly.back();
236         // because we compare with vSize2 here (no division by zero), we also need to compare by vSize2 inside the loop
237         // to avoid integer rounding edge cases
238         bool projected_p_beyond_prev_segment = dot(p1 - p0, from - p0) >= vSize2(p1 - p0);
239         for(const Point& p2 : poly)
240         {
241             // X = A + Normal(B-A) * (((B-A) dot (P-A)) / VSize(B-A));
242             //   = A +       (B-A) *  ((B-A) dot (P-A)) / VSize2(B-A);
243             // X = P projected on AB
244             const Point& a = p1;
245             const Point& b = p2;
246             const Point& p = from;
247             Point ab = b - a;
248             Point ap = p - a;
249             int64_t ab_length2 = vSize2(ab);
250             if(ab_length2 <= 0) //A = B, i.e. the input polygon had two adjacent points on top of each other.
251             {
252                 p1 = p2; //Skip only one of the points.
253                 continue;
254             }
255             int64_t dot_prod = dot(ab, ap);
256             if (dot_prod <= 0) // x is projected to before ab
257             {
258                 if (projected_p_beyond_prev_segment)
259                 { //  case which looks like:   > .
260                     projected_p_beyond_prev_segment = false;
261                     Point& x = p1;
262 
263                     int64_t dist2 = vSize2(x - p);
264                     if (dist2 < bestDist2)
265                     {
266                         bestDist2 = dist2;
267                         bestPoly = poly_idx;
268                         if (distance == 0) { ret = x; }
269                         else
270                         {
271                             Point inward_dir = turn90CCW(normal(ab, MM2INT(10.0)) + normal(p1 - p0, MM2INT(10.0))); // inward direction irrespective of sign of [distance]
272                             // MM2INT(10.0) to retain precision for the eventual normalization
273                             ret = x + normal(inward_dir, distance);
274                             is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) * distance >= 0;
275                         }
276                     }
277                 }
278                 else
279                 {
280                     projected_p_beyond_prev_segment = false;
281                     p0 = p1;
282                     p1 = p2;
283                     continue;
284                 }
285             }
286             else if (dot_prod >= ab_length2) // x is projected to beyond ab
287             {
288                 projected_p_beyond_prev_segment = true;
289                 p0 = p1;
290                 p1 = p2;
291                 continue;
292             }
293             else
294             { // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
295                 projected_p_beyond_prev_segment = false;
296                 Point x = a + ab * dot_prod / ab_length2;
297 
298                 int64_t dist2 = vSize2(p - x);
299                 if (dist2 < bestDist2)
300                 {
301                     bestDist2 = dist2;
302                     bestPoly = poly_idx;
303                     if (distance == 0) { ret = x; }
304                     else
305                     {
306                         Point inward_dir = turn90CCW(normal(ab, distance)); // inward or outward depending on the sign of [distance]
307                         ret = x + inward_dir;
308                         is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) >= 0;
309                     }
310                 }
311             }
312 
313 
314             p0 = p1;
315             p1 = p2;
316         }
317     }
318     if (is_already_on_correct_side_of_boundary) // when the best point is already inside and we're moving inside, or when the best point is already outside and we're moving outside
319     {
320         if (bestDist2 < distance * distance)
321         {
322             from = ret;
323         }
324         else
325         {
326 //            from = from; // original point stays unaltered. It is already inside by enough distance
327         }
328         return bestPoly;
329     }
330     else if (bestDist2 < maxDist2)
331     {
332         from = ret;
333         return bestPoly;
334     }
335     return NO_INDEX;
336 }
337 
338 //Version that works on single PolygonRef.
moveInside(const ConstPolygonRef polygon,Point & from,int distance,int64_t maxDist2)339 unsigned int PolygonUtils::moveInside(const ConstPolygonRef polygon, Point& from, int distance, int64_t maxDist2)
340 {
341     //TODO: This is copied from the moveInside of Polygons.
342     /*
343     We'd like to use this function as subroutine in moveInside(Polygons...), but
344     then we'd need to recompute the distance of the point to the polygon, which
345     is expensive. Or we need to return the distance. We need the distance there
346     to compare with the distance to other polygons.
347     */
348     Point ret = from;
349     int64_t bestDist2 = std::numeric_limits<int64_t>::max();
350     bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
351 
352     if (polygon.size() < 2)
353     {
354         return 0;
355     }
356     Point p0 = polygon[polygon.size() - 2];
357     Point p1 = polygon.back();
358     // because we compare with vSize2 here (no division by zero), we also need to compare by vSize2 inside the loop
359     // to avoid integer rounding edge cases
360     bool projected_p_beyond_prev_segment = dot(p1 - p0, from - p0) >= vSize2(p1 - p0);
361     for(const Point& p2 : polygon)
362     {
363         // X = A + Normal(B-A) * (((B-A) dot (P-A)) / VSize(B-A));
364         //   = A +       (B-A) *  ((B-A) dot (P-A)) / VSize2(B-A);
365         // X = P projected on AB
366         const Point& a = p1;
367         const Point& b = p2;
368         const Point& p = from;
369         Point ab = b - a;
370         Point ap = p - a;
371         int64_t ab_length2 = vSize2(ab);
372         if(ab_length2 <= 0) //A = B, i.e. the input polygon had two adjacent points on top of each other.
373         {
374             p1 = p2; //Skip only one of the points.
375             continue;
376         }
377         int64_t dot_prod = dot(ab, ap);
378         if (dot_prod <= 0) // x is projected to before ab
379         {
380             if (projected_p_beyond_prev_segment)
381             { //  case which looks like:   > .
382                 projected_p_beyond_prev_segment = false;
383                 Point& x = p1;
384 
385                 int64_t dist2 = vSize2(x - p);
386                 if (dist2 < bestDist2)
387                 {
388                     bestDist2 = dist2;
389                     if (distance == 0)
390                     {
391                         ret = x;
392                     }
393                     else
394                     {
395                         Point inward_dir = turn90CCW(normal(ab, MM2INT(10.0)) + normal(p1 - p0, MM2INT(10.0))); // inward direction irrespective of sign of [distance]
396                         // MM2INT(10.0) to retain precision for the eventual normalization
397                         ret = x + normal(inward_dir, distance);
398                         is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) * distance >= 0;
399                     }
400                 }
401             }
402             else
403             {
404                 projected_p_beyond_prev_segment = false;
405                 p0 = p1;
406                 p1 = p2;
407                 continue;
408             }
409         }
410         else if (dot_prod >= ab_length2) // x is projected to beyond ab
411         {
412             projected_p_beyond_prev_segment = true;
413             p0 = p1;
414             p1 = p2;
415             continue;
416         }
417         else
418         { // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
419             projected_p_beyond_prev_segment = false;
420             Point x = a + ab * dot_prod / ab_length2;
421 
422             int64_t dist2 = vSize2(p - x);
423             if (dist2 < bestDist2)
424             {
425                 bestDist2 = dist2;
426                 if (distance == 0)
427                 {
428                     ret = x;
429                 }
430                 else
431                 {
432                     Point inward_dir = turn90CCW(normal(ab, distance)); // inward or outward depending on the sign of [distance]
433                     ret = x + inward_dir;
434                     is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) >= 0;
435                 }
436             }
437         }
438 
439         p0 = p1;
440         p1 = p2;
441     }
442 
443     if (is_already_on_correct_side_of_boundary) // when the best point is already inside and we're moving inside, or when the best point is already outside and we're moving outside
444     {
445         if (bestDist2 < distance * distance)
446         {
447             from = ret;
448         }
449     }
450     else if (bestDist2 < maxDist2)
451     {
452         from = ret;
453     }
454     return 0;
455 }
456 
moveOutside(const ClosestPolygonPoint & cpp,const int distance)457 Point PolygonUtils::moveOutside(const ClosestPolygonPoint& cpp, const int distance)
458 {
459     return moveInside(cpp, -distance);
460 }
461 
moveInside(const ClosestPolygonPoint & cpp,const int distance)462 Point PolygonUtils::moveInside(const ClosestPolygonPoint& cpp, const int distance)
463 {
464     if (!cpp.isValid())
465     {
466         return no_point;
467     }
468     if (distance == 0)
469     { // the point which is assumed to be on the boundary doesn't have to be moved
470         return cpp.location;
471     }
472     ConstPolygonRef poly = *cpp.poly;
473     unsigned int point_idx = cpp.point_idx;
474     const Point& on_boundary = cpp.location;
475 
476     const Point& p1 = poly[point_idx];
477     unsigned int p2_idx;
478     for (p2_idx = point_idx + 1; p2_idx != point_idx; p2_idx = p2_idx + 1)
479     { // find the next point different from p1
480         if (p2_idx == poly.size())
481         {
482             p2_idx = 0;
483         }
484         if (poly[p2_idx] != p1)
485         {
486             break;
487         }
488     }
489     const Point& p2 = poly[p2_idx];
490 
491     if (on_boundary == p1)
492     {
493         return getBoundaryPointWithOffset(poly, point_idx, -distance);
494     }
495     else if (on_boundary == p2)
496     {
497         return getBoundaryPointWithOffset(poly, p2_idx, -distance);
498     }
499     else
500     {
501         const Point& x = on_boundary; // on_boundary is already projected on p1-p2
502 
503         Point inward_dir = turn90CCW(normal(p2 - p1, distance));
504         return x + inward_dir;
505     }
506 }
507 
ensureInsideOrOutside(const Polygons & polygons,Point & from,int preferred_dist_inside,int64_t max_dist2,const Polygons * loc_to_line_polygons,const LocToLineGrid * loc_to_line_grid,const std::function<int (Point)> & penalty_function)508 ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons, Point& from, int preferred_dist_inside, int64_t max_dist2, const Polygons* loc_to_line_polygons, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
509 {
510     const ClosestPolygonPoint closest_polygon_point = moveInside2(polygons, from, preferred_dist_inside, max_dist2, loc_to_line_polygons, loc_to_line_grid, penalty_function);
511     return ensureInsideOrOutside(polygons, from, closest_polygon_point, preferred_dist_inside, loc_to_line_polygons, loc_to_line_grid, penalty_function);
512 }
513 
ensureInsideOrOutside(const Polygons & polygons,Point & from,const ClosestPolygonPoint & closest_polygon_point,int preferred_dist_inside,const Polygons * loc_to_line_polygons,const LocToLineGrid * loc_to_line_grid,const std::function<int (Point)> & penalty_function)514 ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons, Point& from, const ClosestPolygonPoint& closest_polygon_point, int preferred_dist_inside, const Polygons* loc_to_line_polygons, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
515 {
516     if (!closest_polygon_point.isValid())
517     {
518         return ClosestPolygonPoint(); // we couldn't move inside
519     }
520     ConstPolygonRef closest_poly = *closest_polygon_point.poly;
521     bool is_outside_boundary = closest_poly.orientation();
522 
523     {
524         bool is_inside = closest_poly.inside(from) == is_outside_boundary; // inside a hole is outside the part
525         if (is_inside == (preferred_dist_inside > 0))
526         { // we ended up on the right side of the polygon
527             // assume we didn't overshoot another polygon in [polygons]
528             return closest_polygon_point;
529         }
530     }
531 
532     // try once more with half the preferred distance inside
533     int64_t max_dist2_here = std::numeric_limits<int64_t>::max(); // we already concluded we are close enough to the closest_poly when we obtained the closest_polygon_point
534     moveInside2(*loc_to_line_polygons, closest_poly, from, preferred_dist_inside / 2, max_dist2_here, loc_to_line_grid, penalty_function);
535     bool is_inside = closest_poly.inside(from) == is_outside_boundary; // inside a hole is outside the part
536     if (is_inside == (preferred_dist_inside > 0))
537     { // we ended up on the right side of the polygon
538         // assume we didn't overshoot another polygon in [polygons]
539         return closest_polygon_point;
540     }
541     // if above fails, we perform an offset and sit directly on the offsetted polygon (and keep the result from the above moveInside)
542     // The offset is performed on the closest reference polygon in order to save computation time
543     else
544     {
545         const coord_t offset = (is_outside_boundary) ? -preferred_dist_inside : preferred_dist_inside; // perform inset on outer boundary and outset on holes
546         Polygons insetted = closest_poly.offset(offset / 2); // perform less inset, because chances are (thin parts of) the polygon will disappear, given that moveInside did an overshoot
547         if (insetted.size() == 0)
548         {
549             return ClosestPolygonPoint(); // we couldn't move inside
550         }
551         ClosestPolygonPoint inside = findClosest(from, insetted, penalty_function);
552         if (inside.isValid())
553         {
554             bool is_inside = polygons.inside(inside.location);
555             if (is_inside != (preferred_dist_inside > 0))
556             {
557                 // Insetting from the reference polygon ended up outside another polygon.
558                 // Perform an offset on all polygons instead.
559                 Polygons all_insetted = polygons.offset(-preferred_dist_inside);
560                 ClosestPolygonPoint overall_inside = findClosest(from, all_insetted, penalty_function);
561                 bool overall_is_inside = polygons.inside(overall_inside.location);
562                 if (overall_is_inside != (preferred_dist_inside > 0))
563                 {
564 #ifdef DEBUG
565                     try
566                     {
567                         int offset_performed = offset / 2;
568                         AABB aabb(polygons);
569                         aabb.expand(std::abs(preferred_dist_inside) * 2);
570                         SVG svg("debug.html", aabb);
571                         svg.writeComment("Original polygon in black");
572                         svg.writePolygons(polygons, SVG::Color::BLACK);
573                         for (auto poly : polygons)
574                         {
575                             for (auto point : poly)
576                             {
577                                 svg.writePoint(point, true, 2);
578                             }
579                         }
580                         std::stringstream ss;
581                         svg.writeComment("Reference polygon in yellow");
582                         svg.writePolygon(closest_poly, SVG::Color::YELLOW);
583                         ss << "Offsetted polygon in blue with offset " << offset_performed;
584                         svg.writeComment(ss.str());
585                         svg.writePolygons(insetted, SVG::Color::BLUE);
586                         for (auto poly : insetted)
587                         {
588                             for (auto point : poly)
589                             {
590                                 svg.writePoint(point, true, 2);
591                             }
592                         }
593                         svg.writeComment("From location");
594                         svg.writePoint(from, true, 5, SVG::Color::GREEN);
595                         svg.writeComment("Location computed to be inside the black polygon");
596                         svg.writePoint(inside.location, true, 5, SVG::Color::RED);
597                     }
598                     catch(...)
599                     {
600                     }
601                     logError("Clipper::offset failed. See generated debug.html!\n\tBlack is original\n\tBlue is offsetted polygon\n");
602 #endif
603                     return ClosestPolygonPoint();
604                 }
605                 inside = overall_inside;
606             }
607             from = inside.location;
608         } // otherwise we just return the closest polygon point without modifying the from location
609         return closest_polygon_point; // don't return a ClosestPolygonPoint with a reference to the above local polygons variable
610     }
611 }
612 
613 
614 
findConnection(ConstPolygonRef poly1,Polygons & polys2,coord_t min_connection_length,coord_t max_connection_length,std::function<bool (std::pair<ClosestPolygonPoint,ClosestPolygonPoint>)> precondition)615 std::pair<ClosestPolygonPoint, ClosestPolygonPoint> PolygonUtils::findConnection(ConstPolygonRef poly1, Polygons& polys2, coord_t min_connection_length, coord_t max_connection_length, std::function<bool (std::pair<ClosestPolygonPoint, ClosestPolygonPoint>)> precondition)
616 {
617     ClosestPolygonPoint invalid;
618     std::pair<ClosestPolygonPoint, ClosestPolygonPoint> ret = std::make_pair(invalid, invalid);
619     if (poly1.empty() || polys2.empty())
620     {
621         return ret;
622     }
623 
624     const coord_t min_connection_dist2 = min_connection_length * min_connection_length;
625     const coord_t max_connection_dist2 = max_connection_length * max_connection_length;
626 
627     LocToLineGrid* grid = PolygonUtils::createLocToLineGrid(polys2, max_connection_length);
628 
629 
630     std::unordered_set<std::pair<size_t, PolygonsPointIndex>> checked_segment_pairs; // pairs of index into segment start on poly1 and PolygonsPointIndex to segment start on polys2
631 
632     for (size_t point_idx = 0; point_idx < poly1.size(); point_idx++)
633     {
634         std::function<bool (const PolygonsPointIndex&)> process_elem_func =
635             [&, point_idx](const PolygonsPointIndex& line_from)
636             {
637                 std::pair<size_t, PolygonsPointIndex> segment_pair = std::make_pair(point_idx, line_from);
638                 if (checked_segment_pairs.count(segment_pair) > 0)
639                 { // these two line segments were already checked
640                     return true; // continue looking for connections
641                 }
642 
643                 Point a1 = poly1[point_idx];
644                 Point a2 = poly1[(point_idx + 1) % poly1.size()];
645                 Point b1 = line_from.p();
646                 Point b2 = line_from.next().p();
647                 std::pair<Point, Point> connection = LinearAlg2D::getClosestConnection(a1, a2, b1, b2);
648                 coord_t dist2 = vSize2(connection.first - connection.second);
649                 ret = std::make_pair(
650                     ClosestPolygonPoint(connection.first, point_idx, poly1),
651                     ClosestPolygonPoint(connection.second, line_from.point_idx, polys2[line_from.poly_idx], line_from.poly_idx));
652                 if (min_connection_dist2 < dist2 && dist2 < max_connection_dist2
653                     && precondition(ret))
654                 {
655                     return false; // stop the search; break the for-loop
656                 }
657 
658                 checked_segment_pairs.emplace(point_idx, line_from);
659                 return true; // continue looking for connections
660             };
661 
662         std::pair<Point, Point> line = std::make_pair(poly1[point_idx], poly1[(point_idx + 1) % poly1.size()]);
663         Point normal_vector = normal(turn90CCW(line.second - line.first), max_connection_length);
664         std::pair<Point, Point> line2 = std::make_pair(line.first + normal_vector, line.second + normal_vector); // for neighborhood around the line
665         std::pair<Point, Point> line3 = std::make_pair(line.first - normal_vector, line.second - normal_vector); // for neighborhood around the line
666 
667         bool continue_;
668         continue_ = grid->processLine(line, process_elem_func);
669         if (!continue_) break;
670         continue_ = grid->processLine(line2, process_elem_func);
671         if (!continue_) break;
672         continue_ = grid->processLine(line3, process_elem_func);
673         if (!continue_) break;
674     }
675     ret.first.poly_idx = 0;
676     delete grid;
677     return ret;
678 }
679 
findSmallestConnection(ClosestPolygonPoint & poly1_result,ClosestPolygonPoint & poly2_result)680 void PolygonUtils::findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result)
681 {
682     if (!poly1_result.poly || !poly2_result.poly)
683     {
684         return;
685     }
686     ConstPolygonRef poly1 = *poly1_result.poly;
687     ConstPolygonRef poly2 = *poly2_result.poly;
688     if (poly1.size() == 0 || poly2.size() == 0)
689     {
690         return;
691     }
692 
693     Point center1 = poly1[0];
694     ClosestPolygonPoint intermediate_poly2_result = findClosest(center1, poly2);
695     ClosestPolygonPoint intermediate_poly1_result = findClosest(intermediate_poly2_result.p(), poly1);
696 
697     poly2_result = findClosest(intermediate_poly1_result.p(), poly2);
698     poly1_result = findClosest(poly2_result.p(), poly1);
699     walkToNearestSmallestConnection(poly1_result, poly2_result);
700 }
701 
walkToNearestSmallestConnection(ClosestPolygonPoint & poly1_result,ClosestPolygonPoint & poly2_result)702 void PolygonUtils::walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result)
703 {
704     if (!poly1_result.isValid() || !poly2_result.isValid())
705     {
706         return;
707     }
708     ConstPolygonRef poly1 = *poly1_result.poly;
709     ConstPolygonRef poly2 = *poly2_result.poly;
710     size_t poly1_idx = poly1_result.poly_idx;
711     size_t poly2_idx = poly2_result.poly_idx;
712     if (poly1_result.point_idx == NO_INDEX || poly2_result.point_idx == NO_INDEX)
713     {
714         return;
715     }
716 
717     int equilibirum_limit = 100; // hard coded value
718     for (int loop_counter = 0; loop_counter < equilibirum_limit; loop_counter++)
719     {
720         unsigned int pos1_before = poly1_result.point_idx;
721         poly1_result = findNearestClosest(poly2_result.location, poly1, poly1_result.point_idx);
722         unsigned int pos2_before = poly2_result.point_idx;
723         poly2_result = findNearestClosest(poly1_result.location, poly2, poly2_result.point_idx);
724 
725         if (poly1_result.point_idx == pos1_before && poly2_result.point_idx == pos2_before)
726         {
727             break;
728         }
729     }
730 
731     // check surrounding verts in order to prevent local optima like the following:
732     //o      o
733     // \.....|
734     //  \_.-'|
735     //   \---|
736     //    \-'|
737     //     o o >> should find connection here
738     coord_t best_distance2 = vSize2(poly1_result.p() - poly2_result.p());
739     auto check_neighboring_vert = [&best_distance2](ConstPolygonRef from_poly, ConstPolygonRef to_poly, ClosestPolygonPoint& from_poly_result, ClosestPolygonPoint& to_poly_result, bool vertex_after)
740         {
741             const Point after_poly2_result = to_poly[(to_poly_result.point_idx + vertex_after) % to_poly.size()];
742             const ClosestPolygonPoint poly1_after_poly2_result = findNearestClosest(after_poly2_result, from_poly, from_poly_result.point_idx);
743             const coord_t poly1_after_poly2_result_dist2 = vSize2(poly1_after_poly2_result.p() - after_poly2_result);
744             if (poly1_after_poly2_result_dist2 < best_distance2)
745             {
746                 from_poly_result = poly1_after_poly2_result;
747                 to_poly_result.location = after_poly2_result;
748                 best_distance2 = poly1_after_poly2_result_dist2;
749             }
750         };
751     check_neighboring_vert(poly1, poly2, poly1_result, poly2_result, false);
752     check_neighboring_vert(poly1, poly2, poly1_result, poly2_result, true);
753     check_neighboring_vert(poly2, poly1, poly2_result, poly1_result, false);
754     check_neighboring_vert(poly2, poly1, poly2_result, poly1_result, true);
755 
756     poly1_result.poly_idx = poly1_idx;
757     poly2_result.poly_idx = poly2_idx;
758 }
759 
findNearestClosest(Point from,ConstPolygonRef polygon,int start_idx)760 ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, ConstPolygonRef polygon, int start_idx)
761 {
762     ClosestPolygonPoint forth = findNearestClosest(from, polygon, start_idx, 1);
763     if (!forth.isValid())
764     {
765         return forth; // stop computation
766     }
767     ClosestPolygonPoint back = findNearestClosest(from, polygon, start_idx, -1);
768     assert(back.isValid());
769     if (vSize2(forth.location - from) < vSize2(back.location - from))
770     {
771         return forth;
772     }
773     else
774     {
775         return back;
776     }
777 }
778 
findNearestClosest(Point from,ConstPolygonRef polygon,int start_idx,int direction)779 ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, ConstPolygonRef polygon, int start_idx, int direction)
780 {
781     if (polygon.size() == 0)
782     {
783         return ClosestPolygonPoint(polygon);
784     }
785     Point aPoint = polygon[0];
786     Point best = aPoint;
787 
788     int64_t closestDist = vSize2(from - best);
789     int bestPos = 0;
790 
791     size_t poly_size = polygon.size();
792     for (size_t p = 0; p < poly_size; p++)
793     {
794         int p1_idx = (poly_size + direction * p + start_idx) % poly_size;
795         int p2_idx = (poly_size + direction * (p + 1) + start_idx) % poly_size;
796         const Point& p1 = polygon[p1_idx];
797         const Point& p2 = polygon[p2_idx];
798 
799         Point closest_here = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
800         int64_t dist = vSize2(from - closest_here);
801         if (dist < closestDist)
802         {
803             best = closest_here;
804             closestDist = dist;
805             bestPos = (direction > 0) ? p1_idx : p2_idx;
806         }
807         else
808         {
809             return ClosestPolygonPoint(best, bestPos, polygon);
810         }
811     }
812 
813     return ClosestPolygonPoint(best, bestPos, polygon);
814 }
815 
findClosest(Point from,const Polygons & polygons,const std::function<int (Point)> & penalty_function)816 ClosestPolygonPoint PolygonUtils::findClosest(Point from, const Polygons& polygons, const std::function<int(Point)>& penalty_function)
817 {
818     ClosestPolygonPoint none;
819 
820     if (polygons.size() == 0)
821     {
822         return none;
823     }
824     ConstPolygonPointer any_polygon = polygons[0];
825     unsigned int any_poly_idx;
826     for (any_poly_idx = 0; any_poly_idx < polygons.size(); any_poly_idx++)
827     { // find first point in all polygons
828         if (polygons[any_poly_idx].size() > 0)
829         {
830             any_polygon = polygons[any_poly_idx];
831             break;
832         }
833     }
834     if (any_polygon->size() == 0)
835     {
836         return none;
837     }
838     ClosestPolygonPoint best((*any_polygon)[0], 0, *any_polygon, any_poly_idx);
839 
840     int64_t closestDist2_score = vSize2(from - best.location) + penalty_function(best.location);
841 
842     for (unsigned int ply = 0; ply < polygons.size(); ply++)
843     {
844         ConstPolygonRef poly = polygons[ply];
845         if (poly.size() == 0) continue;
846         ClosestPolygonPoint closest_here = findClosest(from, poly, penalty_function);
847         if (!closest_here.isValid())
848         {
849             continue;
850         }
851         int64_t dist2_score = vSize2(from - closest_here.location) + penalty_function(closest_here.location);
852         if (dist2_score < closestDist2_score)
853         {
854             best = closest_here;
855             closestDist2_score = dist2_score;
856             best.poly_idx = ply;
857         }
858     }
859 
860     return best;
861 }
862 
findClosest(Point from,ConstPolygonRef polygon,const std::function<int (Point)> & penalty_function)863 ClosestPolygonPoint PolygonUtils::findClosest(Point from, ConstPolygonRef polygon, const std::function<int(Point)>& penalty_function)
864 {
865     if (polygon.size() == 0)
866     {
867         return ClosestPolygonPoint(polygon);
868     }
869     Point aPoint = polygon[0];
870     Point best = aPoint;
871 
872     int64_t closestDist2_score = vSize2(from - best) + penalty_function(best);
873     int bestPos = 0;
874 
875     for (unsigned int p = 0; p<polygon.size(); p++)
876     {
877         const Point& p1 = polygon[p];
878 
879         unsigned int p2_idx = p+1;
880         if (p2_idx >= polygon.size()) p2_idx = 0;
881         const Point& p2 = polygon[p2_idx];
882 
883         Point closest_here = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
884         int64_t dist2_score = vSize2(from - closest_here) + penalty_function(closest_here);
885         if (dist2_score < closestDist2_score)
886         {
887             best = closest_here;
888             closestDist2_score = dist2_score;
889             bestPos = p;
890         }
891     }
892 
893     return ClosestPolygonPoint(best, bestPos, polygon);
894 }
895 
findNearestVert(const Point from,const Polygons & polys)896 PolygonsPointIndex PolygonUtils::findNearestVert(const Point from, const Polygons& polys)
897 {
898     int64_t best_dist2 = std::numeric_limits<int64_t>::max();
899     PolygonsPointIndex closest_vert;
900     for (unsigned int poly_idx = 0; poly_idx < polys.size(); poly_idx++)
901     {
902         ConstPolygonRef poly = polys[poly_idx];
903         for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
904         {
905             int64_t dist2 = vSize2(poly[point_idx] - from);
906             if (dist2 < best_dist2)
907             {
908                 best_dist2 = dist2;
909                 closest_vert = PolygonsPointIndex(&polys, poly_idx, point_idx);
910             }
911         }
912     }
913     return closest_vert;
914 }
915 
findNearestVert(const Point from,ConstPolygonRef poly)916 unsigned int PolygonUtils::findNearestVert(const Point from, ConstPolygonRef poly)
917 {
918     int64_t best_dist2 = std::numeric_limits<int64_t>::max();
919     unsigned int closest_vert_idx = -1;
920     for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
921     {
922         int64_t dist2 = vSize2(poly[point_idx] - from);
923         if (dist2 < best_dist2)
924         {
925             best_dist2 = dist2;
926             closest_vert_idx = point_idx;
927         }
928     }
929     return closest_vert_idx;
930 }
931 
932 
createLocToLineGrid(const Polygons & polygons,int square_size)933 LocToLineGrid* PolygonUtils::createLocToLineGrid(const Polygons& polygons, int square_size)
934 {
935     unsigned int n_points = 0;
936     for (const auto& poly : polygons)
937     {
938         n_points += poly.size();
939     }
940 
941     LocToLineGrid* ret = new LocToLineGrid(square_size, n_points);
942 
943     for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
944     {
945         ConstPolygonRef poly = polygons[poly_idx];
946         for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
947         {
948             ret->insert(PolygonsPointIndex(&polygons, poly_idx, point_idx));
949         }
950 
951     }
952     return ret;
953 }
954 
955 /*
956  * The current implemetnation can check the same line segment multiple times,
957  * since the same line segment can occur in multiple cells if it it longer than the cell size of the SparsePointGridInclusive.
958  *
959  * We could skip the duplication by keeping a vector of vectors of bools.
960  *
961  */
findClose(Point from,const Polygons & polygons,const LocToLineGrid & loc_to_line,const std::function<int (Point)> & penalty_function)962 std::optional<ClosestPolygonPoint> PolygonUtils::findClose(
963     Point from, const Polygons& polygons,
964     const LocToLineGrid& loc_to_line,
965     const std::function<int(Point)>& penalty_function)
966 {
967     std::vector<PolygonsPointIndex> near_lines =
968         loc_to_line.getNearby(from, loc_to_line.getCellSize());
969 
970     Point best(0, 0);
971 
972     int64_t closest_dist2_score = std::numeric_limits<int64_t>::max();
973     PolygonsPointIndex best_point_poly_idx(nullptr, NO_INDEX, NO_INDEX);
974     for (PolygonsPointIndex& point_poly_index : near_lines)
975     {
976         ConstPolygonRef poly = polygons[point_poly_index.poly_idx];
977         const Point& p1 = poly[point_poly_index.point_idx];
978         const Point& p2 = poly[(point_poly_index.point_idx + 1) % poly.size()];
979 
980         Point closest_here = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
981         int64_t dist2_score = vSize2(from - closest_here) + penalty_function(closest_here);
982         if (dist2_score < closest_dist2_score)
983         {
984             best = closest_here;
985             closest_dist2_score = dist2_score;
986             best_point_poly_idx = point_poly_index;
987         }
988     }
989     if (best_point_poly_idx.poly_idx == NO_INDEX)
990     {
991         return std::optional<ClosestPolygonPoint>();
992     }
993     else
994     {
995         bool bs_arg = true; // doesn't mean anything. Just to make clear we call the variable arguments of the constructor.
996         return std::optional<ClosestPolygonPoint>(bs_arg, best, best_point_poly_idx.point_idx, polygons[best_point_poly_idx.poly_idx], best_point_poly_idx.poly_idx);
997     }
998 }
999 
1000 
findClose(ConstPolygonRef from,const Polygons & destination,const LocToLineGrid & destination_loc_to_line,const std::function<int (Point)> & penalty_function)1001 std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> PolygonUtils::findClose(
1002     ConstPolygonRef from, const Polygons& destination,
1003     const LocToLineGrid& destination_loc_to_line,
1004     const std::function<int(Point)>& penalty_function)
1005 {
1006     std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> ret;
1007     int p0_idx = from.size() - 1;
1008     Point p0(from[p0_idx]);
1009     int grid_size = destination_loc_to_line.getCellSize();
1010     for (unsigned int p1_idx = 0; p1_idx < from.size(); p1_idx++)
1011     {
1012         const Point& p1 = from[p1_idx];
1013         std::optional<ClosestPolygonPoint> best_here = findClose(p1, destination, destination_loc_to_line, penalty_function);
1014         if (best_here)
1015         {
1016             ret.push_back(std::make_pair(ClosestPolygonPoint(p1, p1_idx, from), *best_here));
1017         }
1018         Point p0p1 = p1 - p0;
1019         int dist_to_p1 = vSize(p0p1);
1020         for (unsigned int middle_point_nr = 1; dist_to_p1 > grid_size * 2; ++middle_point_nr)
1021         {
1022             Point x = p0 + normal(p0p1, middle_point_nr * grid_size);
1023             dist_to_p1 -= grid_size;
1024 
1025             std::optional<ClosestPolygonPoint> best_here = findClose(x, destination, destination_loc_to_line, penalty_function);
1026             if (best_here)
1027             {
1028                 ret.push_back(std::make_pair(ClosestPolygonPoint(x, p0_idx, from), *best_here));
1029             }
1030         }
1031         p0 = p1;
1032         p0_idx = p1_idx;
1033     }
1034     return ret;
1035 }
1036 
1037 
1038 
1039 
1040 
getNextPointWithDistance(Point from,int64_t dist,ConstPolygonRef poly,int start_idx,int poly_start_idx,GivenDistPoint & result)1041 bool PolygonUtils::getNextPointWithDistance(Point from, int64_t dist, ConstPolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result)
1042 {
1043 
1044     Point prev_poly_point = poly[(start_idx + poly_start_idx) % poly.size()];
1045 
1046     for (unsigned int prev_idx = start_idx; prev_idx < poly.size(); prev_idx++)
1047     {
1048         int next_idx = (prev_idx + 1 + poly_start_idx) % poly.size(); // last checked segment is between last point in poly and poly[0]...
1049         const Point& next_poly_point = poly[next_idx];
1050         if ( !shorterThen(next_poly_point - from, dist) )
1051         {
1052             /*
1053              *                 x    r
1054              *      p.---------+---+------------.n
1055              *                L|  /
1056              *                 | / dist
1057              *                 |/
1058              *                f.
1059              *
1060              * f=from
1061              * p=prev_poly_point
1062              * n=next_poly_point
1063              * x= f projected on pn
1064              * r=result point at distance [dist] from f
1065              */
1066 
1067             Point pn = next_poly_point - prev_poly_point;
1068 
1069             if (shorterThen(pn, 100)) // when precision is limited
1070             {
1071                 Point middle = (next_poly_point + prev_poly_point) / 2;
1072                 int64_t dist_to_middle = vSize(from - middle);
1073                 if (dist_to_middle - dist < 100 && dist_to_middle - dist > -100)
1074                 {
1075                     result.location = middle;
1076                     result.pos = prev_idx;
1077                     return true;
1078                 } else
1079                 {
1080                     prev_poly_point = next_poly_point;
1081                     continue;
1082                 }
1083             }
1084 
1085             Point pf = from - prev_poly_point;
1086             Point px = dot(pf, pn) / vSize(pn) * pn / vSize(pn);
1087             Point xf = pf - px;
1088 
1089             if (!shorterThen(xf, dist)) // line lies wholly further than pn
1090             {
1091                 prev_poly_point = next_poly_point;
1092                 continue;
1093 
1094             }
1095 
1096             int64_t xr_dist = std::sqrt(dist*dist - vSize2(xf)); // inverse Pythagoras
1097 
1098             if (vSize(pn - px) - xr_dist < 1) // r lies beyond n
1099             {
1100                 prev_poly_point = next_poly_point;
1101                 continue;
1102             }
1103 
1104             Point xr = xr_dist * pn / vSize(pn);
1105             Point pr = px + xr;
1106 
1107             result.location = prev_poly_point + pr;
1108             result.pos = prev_idx;
1109             return true;
1110         }
1111         prev_poly_point = next_poly_point;
1112     }
1113     return false;
1114 }
1115 
1116 
getNextParallelIntersection(const ClosestPolygonPoint & start,const Point & line_to,const coord_t dist,const bool forward)1117 std::optional<ClosestPolygonPoint> PolygonUtils::getNextParallelIntersection(const ClosestPolygonPoint& start, const Point& line_to, const coord_t dist, const bool forward)
1118 {
1119     // <--o--t-----y----< poly 1
1120     //       :     :
1121     // >---o :====>:shift
1122     //      \:     :
1123     //       s     x
1124     //        \    :
1125     //         \   :
1126     //          o--r----->  poly 2
1127     //  s=start
1128     //  r=result
1129     //  t=line_to
1130 
1131     ConstPolygonRef poly = *start.poly;
1132     const Point s = start.p();
1133     const Point t = line_to;
1134 
1135     const Point st = t - s;
1136     const Point shift = normal(turn90CCW(st), dist);
1137 
1138     Point prev_vert = s;
1139     coord_t prev_projected = 0;
1140     for (unsigned int next_point_nr = 0; next_point_nr < poly.size(); next_point_nr++)
1141     {
1142         const unsigned int next_point_idx =
1143             forward ?
1144                 (start.point_idx + 1 + next_point_nr) % poly.size()
1145                 : (static_cast<size_t>(start.point_idx) - next_point_nr + poly.size()) % poly.size(); // cast in order to accomodate subtracting
1146         const Point next_vert = poly[next_point_idx];
1147         const Point so = next_vert - s;
1148         const coord_t projected = dot(shift, so) / dist;
1149         if (std::abs(projected) > dist)
1150         { // segment crosses the line through xy (or the one on the other side of st)
1151             const Point segment_vector = next_vert - prev_vert;
1152             const coord_t segment_length = vSize(segment_vector);
1153             const coord_t projected_segment_length = std::abs(projected - prev_projected);
1154             const int16_t sign = (projected > 0) ? 1 : -1;
1155             const coord_t projected_inter_segment_length = dist - sign * prev_projected; // add the prev_projected to dist if it is projected to the other side of the input line than where the intersection occurs.
1156             const coord_t inter_segment_length = segment_length * projected_inter_segment_length / projected_segment_length;
1157             const Point intersection = prev_vert + normal(next_vert - prev_vert, inter_segment_length);
1158 
1159             size_t vert_before_idx = next_point_idx;
1160             if (forward)
1161             {
1162                 vert_before_idx = (next_point_idx > 0) ? vert_before_idx - 1 : poly.size() - 1;
1163             }
1164             assert(vert_before_idx < poly.size());
1165             return ClosestPolygonPoint(intersection, vert_before_idx, poly);
1166         }
1167 
1168         prev_vert = next_vert;
1169         prev_projected = projected;
1170     }
1171 
1172     return std::optional<ClosestPolygonPoint>();
1173 }
1174 
1175 
polygonCollidesWithLineSegment(const Point from,const Point to,const LocToLineGrid & loc_to_line,PolygonsPointIndex * collision_result)1176 bool PolygonUtils::polygonCollidesWithLineSegment(const Point from, const Point to, const LocToLineGrid& loc_to_line, PolygonsPointIndex* collision_result)
1177 {
1178     bool ret = false;
1179     Point diff = to - from;
1180     if (vSize2(diff) < 2)
1181     { // transformation matrix would fail
1182         return false;
1183     }
1184 
1185     PointMatrix transformation_matrix = PointMatrix(diff);
1186     Point transformed_from = transformation_matrix.apply(from);
1187     Point transformed_to = transformation_matrix.apply(to);
1188 
1189     PolygonsPointIndex result;
1190 
1191     std::function<bool (const PolygonsPointIndex&)> process_elem_func =
1192         [transformed_from, transformed_to, &transformation_matrix, &result, &ret]
1193         (const PolygonsPointIndex& line_start)
1194         {
1195             Point p0 = transformation_matrix.apply(line_start.p());
1196             Point p1 = transformation_matrix.apply(line_start.next().p());
1197 
1198             if (LinearAlg2D::lineSegmentsCollide(transformed_from, transformed_to, p0, p1))
1199             {
1200                 result = line_start;
1201                 ret = true;
1202                 return false;
1203             }
1204             return true;
1205         };
1206     loc_to_line.processLine(std::make_pair(from, to), process_elem_func);
1207 
1208     if (collision_result)
1209     {
1210         *collision_result = result;
1211     }
1212     return ret;
1213 }
1214 
polygonCollidesWithLineSegment(ConstPolygonRef poly,const Point & transformed_startPoint,const Point & transformed_endPoint,PointMatrix transformation_matrix)1215 bool PolygonUtils::polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& transformed_startPoint, const Point& transformed_endPoint, PointMatrix transformation_matrix)
1216 {
1217     Point p0 = transformation_matrix.apply(poly.back());
1218     for(Point p1_ : poly)
1219     {
1220         Point p1 = transformation_matrix.apply(p1_);
1221         if (LinearAlg2D::lineSegmentsCollide(transformed_startPoint, transformed_endPoint, p0, p1))
1222         {
1223             return true;
1224         }
1225         p0 = p1;
1226     }
1227     return false;
1228 }
1229 
polygonCollidesWithLineSegment(ConstPolygonRef poly,const Point & startPoint,const Point & endPoint)1230 bool PolygonUtils::polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& startPoint, const Point& endPoint)
1231 {
1232     Point diff = endPoint - startPoint;
1233 
1234     PointMatrix transformation_matrix = PointMatrix(diff);
1235     Point transformed_startPoint = transformation_matrix.apply(startPoint);
1236     Point transformed_endPoint = transformation_matrix.apply(endPoint);
1237 
1238     return PolygonUtils::polygonCollidesWithLineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix);
1239 }
1240 
polygonCollidesWithLineSegment(const Polygons & polys,const Point & transformed_startPoint,const Point & transformed_endPoint,PointMatrix transformation_matrix)1241 bool PolygonUtils::polygonCollidesWithLineSegment(const Polygons& polys, const Point& transformed_startPoint, const Point& transformed_endPoint, PointMatrix transformation_matrix)
1242 {
1243     for (ConstPolygonRef poly : polys)
1244     {
1245         if (poly.size() == 0) { continue; }
1246         if (PolygonUtils::polygonCollidesWithLineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix))
1247         {
1248             return true;
1249         }
1250     }
1251 
1252     return false;
1253 }
1254 
1255 
polygonCollidesWithLineSegment(const Polygons & polys,const Point & startPoint,const Point & endPoint)1256 bool PolygonUtils::polygonCollidesWithLineSegment(const Polygons& polys, const Point& startPoint, const Point& endPoint)
1257 {
1258     Point diff = endPoint - startPoint;
1259 
1260     PointMatrix transformation_matrix = PointMatrix(diff);
1261     Point transformed_startPoint = transformation_matrix.apply(startPoint);
1262     Point transformed_endPoint = transformation_matrix.apply(endPoint);
1263 
1264     return polygonCollidesWithLineSegment(polys, transformed_startPoint, transformed_endPoint, transformation_matrix);
1265 }
1266 
polygonsIntersect(const ConstPolygonRef & poly_a,const ConstPolygonRef & poly_b)1267 bool PolygonUtils::polygonsIntersect(const ConstPolygonRef& poly_a, const ConstPolygonRef& poly_b)
1268 {
1269     // only do the full intersection when the polys' BBs overlap
1270     AABB bba(poly_a);
1271     AABB bbb(poly_b);
1272     return bba.hit(bbb) && poly_a.intersection(poly_b).size() > 0;
1273 }
1274 
polygonOutlinesAdjacent(const ConstPolygonRef inner_poly,const ConstPolygonRef outer_poly,const coord_t max_gap)1275 bool PolygonUtils::polygonOutlinesAdjacent(const ConstPolygonRef inner_poly, const ConstPolygonRef outer_poly, const coord_t max_gap)
1276 {
1277     //Heuristic check if their AABBs are near first.
1278     AABB inner_aabb(inner_poly);
1279     AABB outer_aabb(outer_poly);
1280     inner_aabb.max += Point(max_gap, max_gap); //Expand one of them by way of a "distance" by checking intersection with the expanded rectangle.
1281     inner_aabb.min -= Point(max_gap, max_gap);
1282     if (!inner_aabb.hit(outer_aabb))
1283     {
1284         return false;
1285     }
1286 
1287     //Heuristic says they are near. Now check for real.
1288     const coord_t max_gap2 = max_gap * max_gap;
1289     const unsigned outer_poly_size = outer_poly.size();
1290     for (unsigned line_index = 0; line_index < outer_poly_size; ++line_index)
1291     {
1292         const Point lp0 = outer_poly[line_index];
1293         const Point lp1 = outer_poly[(line_index + 1) % outer_poly_size];
1294         for (Point inner_poly_point : inner_poly)
1295         {
1296             if (LinearAlg2D::getDist2FromLineSegment(lp0, inner_poly_point, lp1) < max_gap2)
1297             {
1298                 return true;
1299             }
1300         }
1301     }
1302     return false;
1303 }
1304 
findAdjacentPolygons(std::vector<unsigned> & adjacent_poly_indices,const ConstPolygonRef & poly,const std::vector<ConstPolygonPointer> & possible_adjacent_polys,const coord_t max_gap)1305 void PolygonUtils::findAdjacentPolygons(std::vector<unsigned>& adjacent_poly_indices, const ConstPolygonRef& poly, const std::vector<ConstPolygonPointer>& possible_adjacent_polys, const coord_t max_gap)
1306 {
1307     // given a polygon, and a vector of polygons, return a vector containing the indices of the polygons that are adjacent to the given polygon
1308     for (unsigned poly_idx = 0; poly_idx < possible_adjacent_polys.size(); ++poly_idx)
1309     {
1310         if (polygonOutlinesAdjacent(poly, *possible_adjacent_polys[poly_idx], max_gap) ||
1311             polygonOutlinesAdjacent(*possible_adjacent_polys[poly_idx], poly, max_gap))
1312         {
1313             adjacent_poly_indices.push_back(poly_idx);
1314         }
1315     }
1316 }
1317 
relativeHammingDistance(const Polygons & poly_a,const Polygons & poly_b)1318 double PolygonUtils::relativeHammingDistance(const Polygons& poly_a, const Polygons& poly_b)
1319 {
1320     const double area_a = std::abs(poly_a.area());
1321     const double area_b = std::abs(poly_b.area());
1322     const double total_area = area_a + area_b;
1323 
1324     //If the total area is 0.0, we'd get a division by zero. Instead, only return 0.0 if they are exactly equal.
1325     constexpr bool borders_allowed = true;
1326     if(total_area == 0.0)
1327     {
1328         for(const ConstPolygonRef& polygon_a : poly_a)
1329         {
1330             for(Point point : polygon_a)
1331             {
1332                 if(!poly_b.inside(point, borders_allowed))
1333                 {
1334                     return 1.0;
1335                 }
1336             }
1337         }
1338         for(const ConstPolygonRef& polygon_b : poly_b)
1339         {
1340             for(Point point : polygon_b)
1341             {
1342                 if(!poly_a.inside(point, borders_allowed))
1343                 {
1344                     return 1.0;
1345                 }
1346             }
1347         }
1348         return 0.0; //All points are inside the other polygon, regardless of where the vertices are along the edges.
1349     }
1350 
1351     const Polygons symmetric_difference = poly_a.xorPolygons(poly_b);
1352     const double hamming_distance = symmetric_difference.area();
1353     return hamming_distance / total_area;
1354 }
1355 
1356 }//namespace cura
1357