1 #ifndef GEOMETRIES_NOFITPOLYGON_HPP
2 #define GEOMETRIES_NOFITPOLYGON_HPP
3 
4 #include <algorithm>
5 #include <functional>
6 #include <vector>
7 #include <iterator>
8 
9 #include <libnest2d/geometry_traits.hpp>
10 
11 namespace libnest2d {
12 
13 namespace __nfp {
14 // Do not specialize this...
15 template<class RawShape, class Unit = TCompute<RawShape>>
_vsort(const TPoint<RawShape> & v1,const TPoint<RawShape> & v2)16 inline bool _vsort(const TPoint<RawShape>& v1, const TPoint<RawShape>& v2)
17 {
18     Unit x1 = getX(v1), x2 = getX(v2), y1 = getY(v1), y2 = getY(v2);
19     return y1 == y2 ? x1 < x2 : y1 < y2;
20 }
21 
22 template<class EdgeList, class RawShape, class Vertex = TPoint<RawShape>>
buildPolygon(const EdgeList & edgelist,RawShape & rpoly,Vertex & top_nfp)23 inline void buildPolygon(const EdgeList& edgelist,
24                          RawShape& rpoly,
25                          Vertex& top_nfp)
26 {
27     namespace sl = shapelike;
28 
29     auto& rsh = sl::contour(rpoly);
30 
31     sl::reserve(rsh, 2*edgelist.size());
32 
33     // Add the two vertices from the first edge into the final polygon.
34     sl::addVertex(rsh, edgelist.front().first());
35     sl::addVertex(rsh, edgelist.front().second());
36 
37     // Sorting function for the nfp reference vertex search
38     auto& cmp = _vsort<RawShape>;
39 
40     // the reference (rightmost top) vertex so far
41     top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp );
42 
43     auto tmp = std::next(sl::begin(rsh));
44 
45     // Construct final nfp by placing each edge to the end of the previous
46     for(auto eit = std::next(edgelist.begin());
47         eit != edgelist.end();
48         ++eit)
49     {
50         auto d = *tmp - eit->first();
51         Vertex p = eit->second() + d;
52 
53         sl::addVertex(rsh, p);
54 
55         // Set the new reference vertex
56         if(cmp(top_nfp, p)) top_nfp = p;
57 
58         tmp = std::next(tmp);
59     }
60 
61 }
62 
63 template<class Container, class Iterator = typename Container::iterator>
advance(Iterator & it,Container & cont,bool direction)64 void advance(Iterator& it, Container& cont, bool direction)
65 {
66     int dir = direction ? 1 : -1;
67     if(dir < 0 && it == cont.begin()) it = std::prev(cont.end());
68     else it += dir;
69     if(dir > 0 && it == cont.end()) it = cont.begin();
70 }
71 
72 }
73 
74 /// A collection of static methods for handling the no fit polygon creation.
75 namespace nfp {
76 
77 const double BP2D_CONSTEXPR TwoPi = 2*Pi;
78 
79 /// The complexity level of a polygon that an NFP implementation can handle.
80 enum class NfpLevel: unsigned {
81     CONVEX_ONLY,
82     ONE_CONVEX,
83     BOTH_CONCAVE,
84     ONE_CONVEX_WITH_HOLES,
85     BOTH_CONCAVE_WITH_HOLES
86 };
87 
88 template<class RawShape>
89 using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
90 
91 template<class RawShape> struct MaxNfpLevel {
92     static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
93 };
94 
95 
96 // Shorthand for a pile of polygons
97 template<class RawShape>
98 using Shapes = TMultiShape<RawShape>;
99 
100 /**
101  * Merge a bunch of polygons with the specified additional polygon.
102  *
103  * \tparam RawShape the Polygon data type.
104  * \param shc The pile of polygons that will be unified with sh.
105  * \param sh A single polygon to unify with shc.
106  *
107  * \return A set of polygons that is the union of the input polygons. Note that
108  * mostly it will be a set containing only one big polygon but if the input
109  * polygons are disjunct than the resulting set will contain more polygons.
110  */
111 template<class RawShapes>
merge(const RawShapes &)112 inline RawShapes merge(const RawShapes& /*shc*/)
113 {
114     static_assert(always_false<RawShapes>::value,
115                   "Nfp::merge(shapes, shape) unimplemented!");
116 }
117 
118 /**
119  * Merge a bunch of polygons with the specified additional polygon.
120  *
121  * \tparam RawShape the Polygon data type.
122  * \param shc The pile of polygons that will be unified with sh.
123  * \param sh A single polygon to unify with shc.
124  *
125  * \return A set of polygons that is the union of the input polygons. Note that
126  * mostly it will be a set containing only one big polygon but if the input
127  * polygons are disjunct than the resulting set will contain more polygons.
128  */
129 template<class RawShape>
merge(const TMultiShape<RawShape> & shc,const RawShape & sh)130 inline TMultiShape<RawShape> merge(const TMultiShape<RawShape>& shc,
131                                    const RawShape& sh)
132 {
133     auto m = nfp::merge(shc);
134     m.emplace_back(sh);
135     return nfp::merge(m);
136 }
137 
138 /**
139  * Get the vertex of the polygon that is at the lowest values (bottom) in the Y
140  * axis and if there are more than one vertices on the same Y coordinate than
141  * the result will be the leftmost (with the highest X coordinate).
142  */
143 template<class RawShape>
leftmostDownVertex(const RawShape & sh)144 inline TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
145 {
146 
147     // find min x and min y vertex
148     auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh),
149                                __nfp::_vsort<RawShape>);
150 
151     return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;;
152 }
153 
154 /**
155  * Get the vertex of the polygon that is at the highest values (top) in the Y
156  * axis and if there are more than one vertices on the same Y coordinate than
157  * the result will be the rightmost (with the lowest X coordinate).
158  */
159 template<class RawShape>
rightmostUpVertex(const RawShape & sh)160 TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
161 {
162 
163     // find max x and max y vertex
164     auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh),
165                                __nfp::_vsort<RawShape>);
166 
167     return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;
168 }
169 
170 /**
171  * A method to get a vertex from a polygon that always maintains a relative
172  * position to the coordinate system: It is always the rightmost top vertex.
173  *
174  * This way it does not matter in what order the vertices are stored, the
175  * reference will be always the same for the same polygon.
176  */
177 template<class RawShape>
referenceVertex(const RawShape & sh)178 inline TPoint<RawShape> referenceVertex(const RawShape& sh)
179 {
180     return rightmostUpVertex(sh);
181 }
182 
183 /**
184  * The "trivial" Cuninghame-Green implementation of NFP for convex polygons.
185  *
186  * You can use this even if you provide implementations for the more complex
187  * cases (Through specializing the the NfpImpl struct). Currently, no other
188  * cases are covered in the library.
189  *
190  * Complexity should be no more than nlogn (std::sort) in the number of edges
191  * of the input polygons.
192  *
193  * \tparam RawShape the Polygon data type.
194  * \param sh The stationary polygon
195  * \param cother The orbiting polygon
196  * \return Returns a pair of the NFP and its reference vertex of the two input
197  * polygons which have to be strictly convex. The resulting NFP is proven to be
198  * convex as well in this case.
199  *
200  */
201 template<class RawShape, class Ratio = double>
nfpConvexOnly(const RawShape & sh,const RawShape & other)202 inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
203                                          const RawShape& other)
204 {
205     using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
206     namespace sl = shapelike;
207 
208     RawShape rsh;   // Final nfp placeholder
209     Vertex top_nfp;
210     std::vector<Edge> edgelist;
211 
212     auto cap = sl::contourVertexCount(sh) + sl::contourVertexCount(other);
213 
214     // Reserve the needed memory
215     edgelist.reserve(cap);
216     sl::reserve(rsh, static_cast<unsigned long>(cap));
217 
218     { // place all edges from sh into edgelist
219         auto first = sl::cbegin(sh);
220         auto next = std::next(first);
221 
222         while(next != sl::cend(sh)) {
223             edgelist.emplace_back(*(first), *(next));
224             ++first; ++next;
225         }
226     }
227 
228     { // place all edges from other into edgelist
229         auto first = sl::cbegin(other);
230         auto next = std::next(first);
231 
232         while(next != sl::cend(other)) {
233             edgelist.emplace_back(*(next), *(first));
234             ++first; ++next;
235         }
236     }
237 
238     std::sort(edgelist.begin(), edgelist.end(),
239               [](const Edge& e1, const Edge& e2)
240     {
241         Vertex ax(1, 0); // Unit vector for the X axis
242 
243         // get cectors from the edges
244         Vertex p1 = e1.second() - e1.first();
245         Vertex p2 = e2.second() - e2.first();
246 
247         // Quadrant mapping array. The quadrant of a vector can be determined
248         // from the dot product of the vector and its perpendicular pair
249         // with the unit vector X axis. The products will carry the values
250         // lcos = dot(p, ax) = l * cos(phi) and
251         // lsin = -dotperp(p, ax) = l * sin(phi) where
252         // l is the length of vector p. From the signs of these values we can
253         // construct an index which has the sign of lcos as MSB and the
254         // sign of lsin as LSB. This index can be used to retrieve the actual
255         // quadrant where vector p resides using the following map:
256         // (+ is 0, - is 1)
257         // cos | sin | decimal | quadrant
258         //  +  |  +  |    0    |    0
259         //  +  |  -  |    1    |    3
260         //  -  |  +  |    2    |    1
261         //  -  |  -  |    3    |    2
262         std::array<int, 4> quadrants {0, 3, 1, 2 };
263 
264         std::array<int, 2> q {0, 0}; // Quadrant indices for p1 and p2
265 
266         using TDots = std::array<TCompute<Vertex>, 2>;
267         TDots lcos { pl::dot(p1, ax), pl::dot(p2, ax) };
268         TDots lsin { -pl::dotperp(p1, ax), -pl::dotperp(p2, ax) };
269 
270         // Construct the quadrant indices for p1 and p2
271         for(size_t i = 0; i < 2; ++i)
272             if(lcos[i] == 0) q[i] = lsin[i] > 0 ? 1 : 3;
273             else if(lsin[i] == 0) q[i] = lcos[i] > 0 ? 0 : 2;
274             else q[i] = quadrants[((lcos[i] < 0) << 1) + (lsin[i] < 0)];
275 
276         if(q[0] == q[1]) { // only bother if p1 and p2 are in the same quadrant
277             auto lsq1 = pl::magnsq(p1);     // squared magnitudes, avoid sqrt
278             auto lsq2 = pl::magnsq(p2);     // squared magnitudes, avoid sqrt
279 
280             // We will actually compare l^2 * cos^2(phi) which saturates the
281             // cos function. But with the quadrant info we can get the sign back
282             int sign = q[0] == 1 || q[0] == 2 ? -1 : 1;
283 
284             // If Ratio is an actual rational type, there is no precision loss
285             auto pcos1 = Ratio(lcos[0]) / lsq1 * sign * lcos[0];
286             auto pcos2 = Ratio(lcos[1]) / lsq2 * sign * lcos[1];
287 
288             return q[0] < 2 ? pcos1 < pcos2 : pcos1 > pcos2;
289         }
290 
291         // If in different quadrants, compare the quadrant indices only.
292         return q[0] > q[1];
293     });
294 
295     __nfp::buildPolygon(edgelist, rsh, top_nfp);
296 
297     return {rsh, top_nfp};
298 }
299 
300 template<class RawShape>
nfpSimpleSimple(const RawShape & cstationary,const RawShape & cother)301 NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
302                                            const RawShape& cother)
303 {
304 
305     // Algorithms are from the original algorithm proposed in paper:
306     // https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf
307 
308     // /////////////////////////////////////////////////////////////////////////
309     // Algorithm 1: Obtaining the minkowski sum
310     // /////////////////////////////////////////////////////////////////////////
311 
312     // I guess this is not a full minkowski sum of the two input polygons by
313     // definition. This yields a subset that is compatible with the next 2
314     // algorithms.
315 
316     using Result = NfpResult<RawShape>;
317     using Vertex = TPoint<RawShape>;
318     using Coord = TCoord<Vertex>;
319     using Edge = _Segment<Vertex>;
320     namespace sl = shapelike;
321     using std::signbit;
322     using std::sort;
323     using std::vector;
324     using std::ref;
325     using std::reference_wrapper;
326 
327     // TODO The original algorithms expects the stationary polygon in
328     // counter clockwise and the orbiter in clockwise order.
329     // So for preventing any further complication, I will make the input
330     // the way it should be, than make my way around the orientations.
331 
332     // Reverse the stationary contour to counter clockwise
333     auto stcont = sl::contour(cstationary);
334     {
335         std::reverse(sl::begin(stcont), sl::end(stcont));
336         stcont.pop_back();
337         auto it = std::min_element(sl::begin(stcont), sl::end(stcont),
338                                [](const Vertex& v1, const Vertex& v2) {
339             return getY(v1) < getY(v2);
340         });
341         std::rotate(sl::begin(stcont), it, sl::end(stcont));
342         sl::addVertex(stcont, sl::front(stcont));
343     }
344     RawShape stationary;
345     sl::contour(stationary) = stcont;
346 
347     // Reverse the orbiter contour to counter clockwise
348     auto orbcont = sl::contour(cother);
349     {
350         std::reverse(orbcont.begin(), orbcont.end());
351 
352         // Step 1: Make the orbiter reverse oriented
353 
354         orbcont.pop_back();
355         auto it = std::min_element(orbcont.begin(), orbcont.end(),
356                               [](const Vertex& v1, const Vertex& v2) {
357             return getY(v1) < getY(v2);
358         });
359 
360         std::rotate(orbcont.begin(), it, orbcont.end());
361         orbcont.emplace_back(orbcont.front());
362 
363         for(auto &v : orbcont) v = -v;
364 
365     }
366 
367     // Copy the orbiter (contour only), we will have to work on it
368     RawShape orbiter;
369     sl::contour(orbiter) = orbcont;
370 
371     // An edge with additional data for marking it
372     struct MarkedEdge {
373         Edge e; Radians turn_angle = 0; bool is_turning_point = false;
374         MarkedEdge() = default;
375         MarkedEdge(const Edge& ed, Radians ta, bool tp):
376             e(ed), turn_angle(ta), is_turning_point(tp) {}
377 
378         // debug
379         std::string label;
380     };
381 
382     // Container for marked edges
383     using EdgeList = vector<MarkedEdge>;
384 
385     EdgeList A, B;
386 
387     // This is how an edge list is created from the polygons
388     auto fillEdgeList = [](EdgeList& L, const RawShape& ppoly, int dir) {
389         auto& poly = sl::contour(ppoly);
390 
391         L.reserve(sl::contourVertexCount(poly));
392 
393         if(dir > 0) {
394             auto it = poly.begin();
395             auto nextit = std::next(it);
396 
397             double turn_angle = 0;
398             bool is_turn_point = false;
399 
400             while(nextit != poly.end()) {
401                 L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
402                 it++; nextit++;
403             }
404         } else {
405             auto it = sl::rbegin(poly);
406             auto nextit = std::next(it);
407 
408             double turn_angle = 0;
409             bool is_turn_point = false;
410 
411             while(nextit != sl::rend(poly)) {
412                 L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
413                 it++; nextit++;
414             }
415         }
416 
417         auto getTurnAngle = [](const Edge& e1, const Edge& e2) {
418             auto phi = e1.angleToXaxis();
419             auto phi_prev = e2.angleToXaxis();
420             auto turn_angle = phi-phi_prev;
421             if(turn_angle > Pi) turn_angle -= TwoPi;
422             if(turn_angle < -Pi) turn_angle += TwoPi;
423             return turn_angle;
424         };
425 
426         auto eit = L.begin();
427         auto enext = std::next(eit);
428 
429         eit->turn_angle = getTurnAngle(L.front().e, L.back().e);
430 
431         while(enext != L.end()) {
432             enext->turn_angle = getTurnAngle( enext->e, eit->e);
433             eit->is_turning_point =
434                     signbit(enext->turn_angle) != signbit(eit->turn_angle);
435             ++eit; ++enext;
436         }
437 
438         L.back().is_turning_point = signbit(L.back().turn_angle) !=
439                                     signbit(L.front().turn_angle);
440 
441     };
442 
443     // Step 2: Fill the edgelists
444     fillEdgeList(A, stationary, 1);
445     fillEdgeList(B, orbiter, 1);
446 
447     int i = 1;
448     for(MarkedEdge& me : A) {
449         std::cout << "a" << i << ":\n\t"
450                   << getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t"
451                   << getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t"
452                   << "Turning point: " << (me.is_turning_point ? "yes" : "no")
453                   << std::endl;
454 
455         me.label = "a"; me.label += std::to_string(i);
456         i++;
457     }
458 
459     i = 1;
460     for(MarkedEdge& me : B) {
461         std::cout << "b" << i << ":\n\t"
462                   << getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t"
463                   << getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t"
464                   << "Turning point: " << (me.is_turning_point ? "yes" : "no")
465                   << std::endl;
466         me.label = "b"; me.label += std::to_string(i);
467         i++;
468     }
469 
470     // A reference to a marked edge that also knows its container
471     struct MarkedEdgeRef {
472         reference_wrapper<MarkedEdge> eref;
473         reference_wrapper<vector<MarkedEdgeRef>> container;
474         Coord dir = 1;  // Direction modifier
475 
476         inline Radians angleX() const { return eref.get().e.angleToXaxis(); }
477         inline const Edge& edge() const { return eref.get().e; }
478         inline Edge& edge() { return eref.get().e; }
479         inline bool isTurningPoint() const {
480             return eref.get().is_turning_point;
481         }
482         inline bool isFrom(const vector<MarkedEdgeRef>& cont ) {
483             return &(container.get()) == &cont;
484         }
485         inline bool eq(const MarkedEdgeRef& mr) {
486             return &(eref.get()) == &(mr.eref.get());
487         }
488 
489         MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
490                       reference_wrapper<vector<MarkedEdgeRef>> ec):
491             eref(er), container(ec), dir(1) {}
492 
493         MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
494                       reference_wrapper<vector<MarkedEdgeRef>> ec,
495                       Coord d):
496             eref(er), container(ec), dir(d) {}
497     };
498 
499     using EdgeRefList = vector<MarkedEdgeRef>;
500 
501     // Comparing two marked edges
502     auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) {
503         return e1.angleX() < e2.angleX();
504     };
505 
506     EdgeRefList Aref, Bref;     // We create containers for the references
507     Aref.reserve(A.size()); Bref.reserve(B.size());
508 
509     // Fill reference container for the stationary polygon
510     std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) {
511         Aref.emplace_back( ref(me), ref(Aref) );
512     });
513 
514     // Fill reference container for the orbiting polygon
515     std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) {
516         Bref.emplace_back( ref(me), ref(Bref) );
517     });
518 
519     auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure
520             (const EdgeRefList& Q, const EdgeRefList& R, bool positive)
521     {
522 
523         // Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)"
524         // Sort the containers of edge references and merge them.
525         // Q could be sorted only once and be reused here but we would still
526         // need to merge it with sorted(R).
527 
528         EdgeRefList merged;
529         EdgeRefList S, seq;
530         merged.reserve(Q.size() + R.size());
531 
532         merged.insert(merged.end(), R.begin(), R.end());
533         std::stable_sort(merged.begin(), merged.end(), sortfn);
534         merged.insert(merged.end(), Q.begin(), Q.end());
535         std::stable_sort(merged.begin(), merged.end(), sortfn);
536 
537         // Step 2 "set i = 1, k = 1, direction = 1, s1 = q1"
538         // we don't use i, instead, q is an iterator into Q. k would be an index
539         // into the merged sequence but we use "it" as an iterator for that
540 
541         // here we obtain references for the containers for later comparisons
542         const auto& Rcont = R.begin()->container.get();
543         const auto& Qcont = Q.begin()->container.get();
544 
545         // Set the initial direction
546         Coord dir = 1;
547 
548         // roughly i = 1 (so q = Q.begin()) and s1 = q1 so S[0] = q;
549         if(positive) {
550             auto q = Q.begin();
551             S.emplace_back(*q);
552 
553             // Roughly step 3
554 
555             std::cout << "merged size: " << merged.size() << std::endl;
556             auto mit = merged.begin();
557             for(bool finish = false; !finish && q != Q.end();) {
558                 ++q; // "Set i = i + 1"
559 
560                 while(!finish && mit != merged.end()) {
561                     if(mit->isFrom(Rcont)) {
562                         auto s = *mit;
563                         s.dir = dir;
564                         S.emplace_back(s);
565                     }
566 
567                     if(mit->eq(*q)) {
568                         S.emplace_back(*q);
569                         if(mit->isTurningPoint()) dir = -dir;
570                         if(q == Q.begin()) finish = true;
571                         break;
572                     }
573 
574                     mit += dir;
575     //                __nfp::advance(mit, merged, dir > 0);
576                 }
577             }
578         } else {
579             auto q = Q.rbegin();
580             S.emplace_back(*q);
581 
582             // Roughly step 3
583 
584             std::cout << "merged size: " << merged.size() << std::endl;
585             auto mit = merged.begin();
586             for(bool finish = false; !finish && q != Q.rend();) {
587                 ++q; // "Set i = i + 1"
588 
589                 while(!finish && mit != merged.end()) {
590                     if(mit->isFrom(Rcont)) {
591                         auto s = *mit;
592                         s.dir = dir;
593                         S.emplace_back(s);
594                     }
595 
596                     if(mit->eq(*q)) {
597                         S.emplace_back(*q);
598                         S.back().dir = -1;
599                         if(mit->isTurningPoint()) dir = -dir;
600                         if(q == Q.rbegin()) finish = true;
601                         break;
602                     }
603 
604                     mit += dir;
605             //                __nfp::advance(mit, merged, dir > 0);
606                 }
607             }
608         }
609 
610 
611         // Step 4:
612 
613         // "Let starting edge r1 be in position si in sequence"
614         // whaaat? I guess this means the following:
615         auto it = S.begin();
616         while(!it->eq(*R.begin())) ++it;
617 
618         // "Set j = 1, next = 2, direction = 1, seq1 = si"
619         // we don't use j, seq is expanded dynamically.
620         dir = 1;
621         auto next = std::next(R.begin()); seq.emplace_back(*it);
622 
623         // Step 5:
624         // "If all si edges have been allocated to seqj" should mean that
625         // we loop until seq has equal size with S
626         auto send = it; //it == S.begin() ? it : std::prev(it);
627         while(it != S.end()) {
628             ++it; if(it == S.end()) it = S.begin();
629             if(it == send) break;
630 
631             if(it->isFrom(Qcont)) {
632                 seq.emplace_back(*it); // "If si is from Q, j = j + 1, seqj = si"
633 
634                 // "If si is a turning point in Q,
635                 // direction = - direction, next = next + direction"
636                 if(it->isTurningPoint()) {
637                     dir = -dir;
638                     next += dir;
639 //                    __nfp::advance(next, R, dir > 0);
640                 }
641             }
642 
643             if(it->eq(*next) /*&& dir == next->dir*/) { // "If si = direction.rnext"
644                 // "j = j + 1, seqj = si, next = next + direction"
645                 seq.emplace_back(*it);
646                 next += dir;
647 //                __nfp::advance(next, R, dir > 0);
648             }
649         }
650 
651         return seq;
652     };
653 
654     std::vector<EdgeRefList> seqlist;
655     seqlist.reserve(Bref.size());
656 
657     EdgeRefList Bslope = Bref;  // copy Bref, we will make a slope diagram
658 
659     // make the slope diagram of B
660     std::sort(Bslope.begin(), Bslope.end(), sortfn);
661 
662     auto slopeit = Bslope.begin(); // search for the first turning point
663     while(!slopeit->isTurningPoint() && slopeit != Bslope.end()) slopeit++;
664 
665     if(slopeit == Bslope.end()) {
666         // no turning point means convex polygon.
667         seqlist.emplace_back(mink(Aref, Bref, true));
668     } else {
669         int dir = 1;
670 
671         auto firstturn = Bref.begin();
672         while(!firstturn->eq(*slopeit)) ++firstturn;
673 
674         assert(firstturn != Bref.end());
675 
676         EdgeRefList bgroup; bgroup.reserve(Bref.size());
677         bgroup.emplace_back(*slopeit);
678 
679         auto b_it = std::next(firstturn);
680         while(b_it != firstturn) {
681             if(b_it == Bref.end()) b_it = Bref.begin();
682 
683             while(!slopeit->eq(*b_it)) {
684                 __nfp::advance(slopeit, Bslope, dir > 0);
685             }
686 
687             if(!slopeit->isTurningPoint()) {
688                 bgroup.emplace_back(*slopeit);
689             } else {
690                 if(!bgroup.empty()) {
691                     if(dir > 0) bgroup.emplace_back(*slopeit);
692                     for(auto& me : bgroup) {
693                         std::cout << me.eref.get().label << ", ";
694                     }
695                     std::cout << std::endl;
696                     seqlist.emplace_back(mink(Aref, bgroup, dir == 1 ? true : false));
697                     bgroup.clear();
698                     if(dir < 0) bgroup.emplace_back(*slopeit);
699                 } else {
700                     bgroup.emplace_back(*slopeit);
701                 }
702 
703                 dir *= -1;
704             }
705             ++b_it;
706         }
707     }
708 
709 //    while(it != Bref.end()) // This is step 3 and step 4 in one loop
710 //        if(it->isTurningPoint()) {
711 //            R = {R.last, it++};
712 //            auto seq = mink(Q, R, orientation);
713 
714 //            // TODO step 6 (should be 5 shouldn't it?): linking edges from A
715 //            // I don't get this step
716 
717 //            seqlist.insert(seqlist.end(), seq.begin(), seq.end());
718 //            orientation = !orientation;
719 //        } else ++it;
720 
721 //    if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true);
722 
723     // /////////////////////////////////////////////////////////////////////////
724     // Algorithm 2: breaking Minkowski sums into track line trips
725     // /////////////////////////////////////////////////////////////////////////
726 
727 
728     // /////////////////////////////////////////////////////////////////////////
729     // Algorithm 3: finding the boundary of the NFP from track line trips
730     // /////////////////////////////////////////////////////////////////////////
731 
732 
733     for(auto& seq : seqlist) {
734         std::cout << "seqlist size: " << seq.size() << std::endl;
735         for(auto& s : seq) {
736             std::cout << (s.dir > 0 ? "" : "-") << s.eref.get().label << ", ";
737         }
738         std::cout << std::endl;
739     }
740 
741     auto& seq = seqlist.front();
742     RawShape rsh;
743     Vertex top_nfp;
744     std::vector<Edge> edgelist; edgelist.reserve(seq.size());
745     for(auto& s : seq) {
746         edgelist.emplace_back(s.eref.get().e);
747     }
748 
749     __nfp::buildPolygon(edgelist, rsh, top_nfp);
750 
751     return Result(rsh, top_nfp);
752 }
753 
754 // Specializable NFP implementation class. Specialize it if you have a faster
755 // or better NFP implementation
756 template<class RawShape, NfpLevel nfptype>
757 struct NfpImpl {
operator ()libnest2d::nfp::NfpImpl758     NfpResult<RawShape> operator()(const RawShape& sh, const RawShape& other)
759     {
760         static_assert(nfptype == NfpLevel::CONVEX_ONLY,
761                       "Nfp::noFitPolygon() unimplemented!");
762 
763         // Libnest2D has a default implementation for convex polygons and will
764         // use it if feasible.
765         return nfpConvexOnly(sh, other);
766     }
767 };
768 
769 /// Helper function to get the NFP
770 template<NfpLevel nfptype, class RawShape>
noFitPolygon(const RawShape & sh,const RawShape & other)771 inline NfpResult<RawShape> noFitPolygon(const RawShape& sh,
772                                         const RawShape& other)
773 {
774     NfpImpl<RawShape, nfptype> nfps;
775     return nfps(sh, other);
776 }
777 
778 }
779 
780 }
781 
782 #endif // GEOMETRIES_NOFITPOLYGON_HPP
783