File indexing completed on 2024-04-28 15:17:11

0001 /*******************************************************************************
0002 * Author    :  Angus Johnson                                                   *
0003 * Date      :  16 July 2023                                                    *
0004 * Website   :  http://www.angusj.com                                           *
0005 * Copyright :  Angus Johnson 2010-2023                                         *
0006 * Purpose   :  This module provides a simple interface to the Clipper Library  *
0007 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
0008 *******************************************************************************/
0009 
0010 #ifndef CLIPPER_H
0011 #define CLIPPER_H
0012 
0013 #include <cstdlib>
0014 #include <type_traits>
0015 #include <vector>
0016 
0017 #include "clipper.core.h"
0018 #include "clipper.engine.h"
0019 #include "clipper.offset.h"
0020 #include "clipper.minkowski.h"
0021 #include "clipper.rectclip.h"
0022 
0023 namespace Clipper2Lib {
0024 
0025   inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
0026     const Paths64& subjects, const Paths64& clips)
0027   {    
0028     Paths64 result;
0029     Clipper64 clipper;
0030     clipper.AddSubject(subjects);
0031     clipper.AddClip(clips);
0032     clipper.Execute(cliptype, fillrule, result);
0033     return result;
0034   }
0035 
0036   inline void BooleanOp(ClipType cliptype, FillRule fillrule,
0037     const Paths64& subjects, const Paths64& clips, PolyTree64& solution)
0038   {
0039     Paths64 sol_open;
0040     Clipper64 clipper;
0041     clipper.AddSubject(subjects);
0042     clipper.AddClip(clips);
0043     clipper.Execute(cliptype, fillrule, solution, sol_open);
0044   }
0045 
0046   inline PathsD BooleanOp(ClipType cliptype, FillRule fillrule,
0047     const PathsD& subjects, const PathsD& clips, int precision = 2)
0048   {
0049     int error_code = 0;
0050     CheckPrecision(precision, error_code);
0051     PathsD result;
0052     if (error_code) return result;
0053     ClipperD clipper(precision);
0054     clipper.AddSubject(subjects);
0055     clipper.AddClip(clips);
0056     clipper.Execute(cliptype, fillrule, result);
0057     return result;
0058   }
0059 
0060   inline void BooleanOp(ClipType cliptype, FillRule fillrule,
0061     const PathsD& subjects, const PathsD& clips, 
0062     PolyTreeD& polytree, int precision = 2)
0063   {
0064     polytree.Clear();
0065     int error_code = 0;
0066     CheckPrecision(precision, error_code);
0067     if (error_code) return;
0068     ClipperD clipper(precision);
0069     clipper.AddSubject(subjects);
0070     clipper.AddClip(clips);
0071     clipper.Execute(cliptype, fillrule, polytree);
0072   }
0073 
0074   inline Paths64 Intersect(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
0075   {
0076     return BooleanOp(ClipType::Intersection, fillrule, subjects, clips);
0077   }
0078   
0079   inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
0080   {
0081     return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec);
0082   }
0083 
0084   inline Paths64 Union(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
0085   {
0086     return BooleanOp(ClipType::Union, fillrule, subjects, clips);
0087   }
0088 
0089   inline PathsD Union(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
0090   {
0091     return BooleanOp(ClipType::Union, fillrule, subjects, clips, decimal_prec);
0092   }
0093 
0094   inline Paths64 Union(const Paths64& subjects, FillRule fillrule)
0095   {
0096     Paths64 result;
0097     Clipper64 clipper;
0098     clipper.AddSubject(subjects);
0099     clipper.Execute(ClipType::Union, fillrule, result);
0100     return result;
0101   }
0102 
0103   inline PathsD Union(const PathsD& subjects, FillRule fillrule, int precision = 2)
0104   {
0105     PathsD result;
0106     int error_code = 0;
0107     CheckPrecision(precision, error_code);
0108     if (error_code) return result;
0109     ClipperD clipper(precision);
0110     clipper.AddSubject(subjects);
0111     clipper.Execute(ClipType::Union, fillrule, result);
0112     return result;
0113   }
0114 
0115   inline Paths64 Difference(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
0116   {
0117     return BooleanOp(ClipType::Difference, fillrule, subjects, clips);
0118   }
0119 
0120   inline PathsD Difference(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
0121   {
0122     return BooleanOp(ClipType::Difference, fillrule, subjects, clips, decimal_prec);
0123   }
0124 
0125   inline Paths64 Xor(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
0126   {
0127     return BooleanOp(ClipType::Xor, fillrule, subjects, clips);
0128   }
0129 
0130   inline PathsD Xor(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
0131   {
0132     return BooleanOp(ClipType::Xor, fillrule, subjects, clips, decimal_prec);
0133   }
0134 
0135   inline Paths64 InflatePaths(const Paths64& paths, double delta,
0136     JoinType jt, EndType et, double miter_limit = 2.0,
0137     double arc_tolerance = 0.0)
0138   {
0139     if (!delta) return paths;
0140     ClipperOffset clip_offset(miter_limit, arc_tolerance);
0141     clip_offset.AddPaths(paths, jt, et);
0142     Paths64 solution;
0143     clip_offset.Execute(delta, solution);
0144     return solution;
0145   }
0146 
0147   inline PathsD InflatePaths(const PathsD& paths, double delta,
0148     JoinType jt, EndType et, double miter_limit = 2.0, 
0149     int precision = 2, double arc_tolerance = 0.0)
0150   {
0151     int error_code = 0;
0152     CheckPrecision(precision, error_code);
0153     if (!delta) return paths;
0154     if (error_code) return PathsD();
0155     const double scale = std::pow(10, precision);
0156     ClipperOffset clip_offset(miter_limit, arc_tolerance);
0157     clip_offset.AddPaths(ScalePaths<int64_t,double>(paths, scale, error_code), jt, et);
0158     if (error_code) return PathsD();
0159     Paths64 solution;
0160     clip_offset.Execute(delta * scale, solution);
0161     return ScalePaths<double, int64_t>(solution, 1 / scale, error_code);
0162   }
0163 
0164   template <typename T>
0165   inline Path<T> TranslatePath(const Path<T>& path, T dx, T dy)
0166   {
0167     Path<T> result;
0168     result.reserve(path.size());
0169     std::transform(path.begin(), path.end(), back_inserter(result),
0170       [dx, dy](const auto& pt) { return Point<T>(pt.x + dx, pt.y +dy); });
0171     return result;
0172   }
0173 
0174   inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy)
0175   {
0176     return TranslatePath<int64_t>(path, dx, dy);
0177   }
0178 
0179   inline PathD TranslatePath(const PathD& path, double dx, double dy)
0180   {
0181     return TranslatePath<double>(path, dx, dy);
0182   }
0183 
0184   template <typename T>
0185   inline Paths<T> TranslatePaths(const Paths<T>& paths, T dx, T dy)
0186   {
0187     Paths<T> result;
0188     result.reserve(paths.size());
0189     std::transform(paths.begin(), paths.end(), back_inserter(result),
0190       [dx, dy](const auto& path) { return TranslatePath(path, dx, dy); });
0191     return result;
0192   }
0193 
0194   inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy)
0195   {
0196     return TranslatePaths<int64_t>(paths, dx, dy);
0197   }
0198 
0199   inline PathsD TranslatePaths(const PathsD& paths, double dx, double dy)
0200   {
0201     return TranslatePaths<double>(paths, dx, dy);
0202   }
0203 
0204   inline Paths64 RectClip(const Rect64& rect, const Paths64& paths)
0205   {
0206     if (rect.IsEmpty() || paths.empty()) return Paths64();
0207     RectClip64 rc(rect);
0208     return rc.Execute(paths);
0209   }
0210 
0211   inline Paths64 RectClip(const Rect64& rect, const Path64& path)
0212   {
0213     if (rect.IsEmpty() || path.empty()) return Paths64();
0214     RectClip64 rc(rect);
0215     return rc.Execute(Paths64{ path });
0216   }
0217 
0218   inline PathsD RectClip(const RectD& rect, const PathsD& paths, int precision = 2)
0219   {
0220     if (rect.IsEmpty() || paths.empty()) return PathsD();
0221     int error_code = 0;
0222     CheckPrecision(precision, error_code);
0223     if (error_code) return PathsD();
0224     const double scale = std::pow(10, precision);
0225     Rect64 r = ScaleRect<int64_t, double>(rect, scale);
0226     RectClip64 rc(r);
0227     Paths64 pp = ScalePaths<int64_t, double>(paths, scale, error_code);
0228     if (error_code) return PathsD(); // ie: error_code result is lost 
0229     return ScalePaths<double, int64_t>(
0230       rc.Execute(pp), 1 / scale, error_code);
0231   }
0232 
0233   inline PathsD RectClip(const RectD& rect, const PathD& path, int precision = 2)
0234   {
0235     return RectClip(rect, PathsD{ path }, precision);
0236   }
0237 
0238   inline Paths64 RectClipLines(const Rect64& rect, const Paths64& lines)
0239   {
0240     if (rect.IsEmpty() || lines.empty()) return Paths64();
0241     RectClipLines64 rcl(rect);
0242     return rcl.Execute(lines);
0243   }
0244 
0245   inline Paths64 RectClipLines(const Rect64& rect, const Path64& line)
0246   {
0247     return RectClipLines(rect, Paths64{ line });
0248   }
0249 
0250   inline PathsD RectClipLines(const RectD& rect, const PathsD& lines, int precision = 2)
0251   {
0252     if (rect.IsEmpty() || lines.empty()) return PathsD();
0253     int error_code = 0;
0254     CheckPrecision(precision, error_code);
0255     if (error_code) return PathsD();
0256     const double scale = std::pow(10, precision);
0257     Rect64 r = ScaleRect<int64_t, double>(rect, scale);
0258     RectClipLines64 rcl(r);
0259     Paths64 p = ScalePaths<int64_t, double>(lines, scale, error_code);
0260     if (error_code) return PathsD();
0261     p = rcl.Execute(p);
0262     return ScalePaths<double, int64_t>(p, 1 / scale, error_code);
0263   }
0264 
0265   inline PathsD RectClipLines(const RectD& rect, const PathD& line, int precision = 2)
0266   {
0267     return RectClipLines(rect, PathsD{ line }, precision);
0268   }
0269 
0270   namespace details
0271   {
0272 
0273     inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths)
0274     {
0275       paths.push_back(polypath.Polygon());
0276       for (const auto& child : polypath)
0277         PolyPathToPaths64(*child, paths);
0278     }
0279 
0280     inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths)
0281     {
0282       paths.push_back(polypath.Polygon());
0283       for (const auto& child : polypath)
0284         PolyPathToPathsD(*child, paths);
0285     }
0286 
0287     inline bool PolyPath64ContainsChildren(const PolyPath64& pp)
0288     {
0289       for (const auto& child : pp)
0290       {
0291         // return false if this child isn't fully contained by its parent
0292 
0293         // checking for a single vertex outside is a bit too crude since 
0294         // it doesn't account for rounding errors. It's better to check 
0295         // for consecutive vertices found outside the parent's polygon.
0296 
0297         int outsideCnt = 0;
0298         for (const Point64& pt : child->Polygon())
0299         {
0300           PointInPolygonResult result = PointInPolygon(pt, pp.Polygon());
0301           if (result == PointInPolygonResult::IsInside) --outsideCnt;
0302           else if (result == PointInPolygonResult::IsOutside) ++outsideCnt;
0303           if (outsideCnt > 1) return false;
0304           else if (outsideCnt < -1) break;
0305         }
0306 
0307         // now check any nested children too
0308         if (child->Count() > 0 && !PolyPath64ContainsChildren(*child))
0309           return false;
0310       }
0311       return true;
0312     }
0313 
0314     static void OutlinePolyPath(std::ostream& os, 
0315       size_t idx, bool isHole, size_t count, const std::string& preamble)
0316     {
0317       std::string plural = (count == 1) ? "." : "s.";
0318       if (isHole)
0319         os << preamble << "+- Hole (" << idx << ") contains " << count <<
0320         " nested polygon" << plural << std::endl;
0321       else
0322         os << preamble << "+- Polygon (" << idx << ") contains " << count <<
0323           " hole" << plural << std::endl;
0324     }
0325 
0326     static void OutlinePolyPath64(std::ostream& os, const PolyPath64& pp,
0327       size_t idx, std::string preamble)
0328     {
0329       OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble);
0330       for (size_t i = 0; i < pp.Count(); ++i)
0331         if (pp.Child(i)->Count())
0332           details::OutlinePolyPath64(os, *pp.Child(i), i, preamble + "  ");
0333     }
0334 
0335     static void OutlinePolyPathD(std::ostream& os, const PolyPathD& pp,
0336       size_t idx, std::string preamble)
0337     {
0338       OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble);
0339       for (size_t i = 0; i < pp.Count(); ++i)
0340         if (pp.Child(i)->Count())
0341           details::OutlinePolyPathD(os, *pp.Child(i), i, preamble + "  ");
0342     }
0343 
0344   } // end details namespace 
0345 
0346   inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
0347   {
0348     std::string plural = (pp.Count() == 1) ? " polygon." : " polygons.";
0349     os << std::endl << "Polytree with " << pp.Count() << plural << std::endl;
0350       for (size_t i = 0; i < pp.Count(); ++i)
0351         if (pp.Child(i)->Count())
0352           details::OutlinePolyPath64(os, *pp.Child(i), i, "  ");
0353     os << std::endl << std::endl;
0354     return os;
0355   }
0356 
0357   inline std::ostream& operator<< (std::ostream& os, const PolyTreeD& pp)
0358   {
0359     std::string plural = (pp.Count() == 1) ? " polygon." : " polygons.";
0360     os << std::endl << "Polytree with " << pp.Count() << plural << std::endl;
0361     for (size_t i = 0; i < pp.Count(); ++i)
0362       if (pp.Child(i)->Count())
0363         details::OutlinePolyPathD(os, *pp.Child(i), i, "  ");
0364     os << std::endl << std::endl;
0365     if (!pp.Level()) os << std::endl;
0366     return os;
0367   }
0368 
0369   inline Paths64 PolyTreeToPaths64(const PolyTree64& polytree)
0370   {
0371     Paths64 result;
0372     for (const auto& child : polytree)
0373       details::PolyPathToPaths64(*child, result);
0374     return result;
0375   }
0376 
0377   inline PathsD PolyTreeToPathsD(const PolyTreeD& polytree)
0378   {
0379     PathsD result;
0380     for (const auto& child : polytree)
0381       details::PolyPathToPathsD(*child, result);
0382     return result;
0383   }
0384 
0385   inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree)
0386   {
0387     for (const auto& child : polytree)
0388       if (child->Count() > 0 && 
0389         !details::PolyPath64ContainsChildren(*child))
0390           return false;
0391     return true;
0392   }
0393 
0394   namespace details {
0395 
0396     template<typename T, typename U>
0397     inline constexpr void MakePathGeneric(const T list, size_t size,
0398       std::vector<U>& result)
0399     {
0400       for (size_t i = 0; i < size; ++i)
0401 #ifdef USINGZ
0402         result[i / 2] = U{list[i], list[++i], 0};
0403 #else
0404         result[i / 2] = U{list[i], list[++i]};
0405 #endif
0406     }
0407 
0408   } // end details namespace
0409 
0410   template<typename T,
0411     typename std::enable_if<
0412       std::is_integral<T>::value &&
0413       !std::is_same<char, T>::value, bool
0414     >::type = true>
0415   inline Path64 MakePath(const std::vector<T>& list)
0416   {
0417     const auto size = list.size() - list.size() % 2;
0418     if (list.size() != size)
0419       DoError(non_pair_error_i);  // non-fatal without exception handling
0420     Path64 result(size / 2);      // else ignores unpaired value
0421     details::MakePathGeneric(list, size, result);
0422     return result;
0423   }
0424 
0425   template<typename T, std::size_t N,
0426     typename std::enable_if<
0427       std::is_integral<T>::value &&
0428       !std::is_same<char, T>::value, bool
0429     >::type = true>
0430   inline Path64 MakePath(const T(&list)[N])
0431   {
0432     // Make the compiler error on unpaired value (i.e. no runtime effects).
0433     static_assert(N % 2 == 0, "MakePath requires an even number of arguments");
0434     Path64 result(N / 2);
0435     details::MakePathGeneric(list, N, result);
0436     return result;
0437   }
0438 
0439   template<typename T,
0440     typename std::enable_if<
0441       std::is_arithmetic<T>::value &&
0442       !std::is_same<char, T>::value, bool
0443     >::type = true>
0444   inline PathD MakePathD(const std::vector<T>& list)
0445   {
0446     const auto size = list.size() - list.size() % 2;
0447     if (list.size() != size)
0448       DoError(non_pair_error_i);  // non-fatal without exception handling
0449     PathD result(size / 2);       // else ignores unpaired value
0450     details::MakePathGeneric(list, size, result);
0451     return result;
0452   }
0453 
0454   template<typename T, std::size_t N,
0455     typename std::enable_if<
0456       std::is_arithmetic<T>::value &&
0457       !std::is_same<char, T>::value, bool
0458     >::type = true>
0459   inline PathD MakePathD(const T(&list)[N])
0460   {
0461     // Make the compiler error on unpaired value (i.e. no runtime effects).
0462     static_assert(N % 2 == 0, "MakePath requires an even number of arguments");
0463     PathD result(N / 2);
0464     details::MakePathGeneric(list, N, result);
0465     return result;
0466   }
0467 
0468 #ifdef USINGZ
0469   template<typename T2, std::size_t N>
0470   inline Path64 MakePathZ(const T2(&list)[N])
0471   {
0472     static_assert(N % 3 == 0 && std::numeric_limits<T2>::is_integer,
0473       "MakePathZ requires integer values in multiples of 3");
0474     std::size_t size = N / 3;
0475     Path64 result(size);
0476     for (size_t i = 0; i < size; ++i)
0477       result[i] = Point64(list[i * 3], 
0478         list[i * 3 + 1], list[i * 3 + 2]);
0479     return result;
0480   }
0481 
0482   template<typename T2, std::size_t N>
0483   inline PathD MakePathZD(const T2(&list)[N])
0484   {
0485     static_assert(N % 3 == 0,
0486       "MakePathZD requires values in multiples of 3");
0487     std::size_t size = N / 3;
0488     PathD result(size);
0489     if constexpr (std::numeric_limits<T2>::is_integer)
0490       for (size_t i = 0; i < size; ++i)
0491         result[i] = PointD(list[i * 3],
0492           list[i * 3 + 1], list[i * 3 + 2]);
0493     else
0494       for (size_t i = 0; i < size; ++i)
0495         result[i] = PointD(list[i * 3], list[i * 3 + 1], 
0496           static_cast<int64_t>(list[i * 3 + 2]));
0497     return result;
0498   }
0499 #endif
0500 
0501   inline Path64 TrimCollinear(const Path64& p, bool is_open_path = false)
0502   {
0503     size_t len = p.size();
0504     if (len < 3)
0505     {
0506       if (!is_open_path || len < 2 || p[0] == p[1]) return Path64();
0507       else return p;
0508     }
0509 
0510     Path64 dst;
0511     dst.reserve(len);
0512     Path64::const_iterator srcIt = p.cbegin(), prevIt, stop = p.cend() - 1;
0513 
0514     if (!is_open_path)
0515     {
0516       while (srcIt != stop && !CrossProduct(*stop, *srcIt, *(srcIt + 1)))
0517         ++srcIt;
0518       while (srcIt != stop && !CrossProduct(*(stop - 1), *stop, *srcIt))
0519         --stop;
0520       if (srcIt == stop) return Path64();
0521     }
0522 
0523     prevIt = srcIt++;
0524     dst.push_back(*prevIt);
0525     for (; srcIt != stop; ++srcIt)
0526     {
0527       if (CrossProduct(*prevIt, *srcIt, *(srcIt + 1)))
0528       {
0529         prevIt = srcIt;
0530         dst.push_back(*prevIt);
0531       }
0532     }
0533 
0534     if (is_open_path)
0535       dst.push_back(*srcIt);
0536     else if (CrossProduct(*prevIt, *stop, dst[0]))
0537       dst.push_back(*stop);
0538     else
0539     {
0540       while (dst.size() > 2 &&
0541         !CrossProduct(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
0542           dst.pop_back();
0543       if (dst.size() < 3) return Path64();
0544     }
0545     return dst;
0546   }
0547 
0548   inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false)
0549   {
0550     int error_code = 0;
0551     CheckPrecision(precision, error_code);
0552     if (error_code) return PathD();
0553     const double scale = std::pow(10, precision);
0554     Path64 p = ScalePath<int64_t, double>(path, scale, error_code);
0555     if (error_code) return PathD();
0556     p = TrimCollinear(p, is_open_path);
0557     return ScalePath<double, int64_t>(p, 1/scale, error_code);
0558   }
0559 
0560   template <typename T>
0561   inline double Distance(const Point<T> pt1, const Point<T> pt2)
0562   {
0563     return std::sqrt(DistanceSqr(pt1, pt2));
0564   }
0565 
0566   template <typename T>
0567   inline double Length(const Path<T>& path, bool is_closed_path = false)
0568   {
0569     double result = 0.0;
0570     if (path.size() < 2) return result;
0571     auto it = path.cbegin(), stop = path.end() - 1;
0572     for (; it != stop; ++it)
0573       result += Distance(*it, *(it + 1));
0574     if (is_closed_path)
0575       result += Distance(*stop, *path.cbegin());
0576     return result;
0577   }
0578 
0579 
0580   template <typename T>
0581   inline bool NearCollinear(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3, double sin_sqrd_min_angle_rads)
0582   {
0583     double cp = std::abs(CrossProduct(pt1, pt2, pt3));
0584     return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads;
0585   }
0586   
0587   template <typename T>
0588   inline Path<T> Ellipse(const Rect<T>& rect, int steps = 0)
0589   {
0590     return Ellipse(rect.MidPoint(), 
0591       static_cast<double>(rect.Width()) *0.5, 
0592       static_cast<double>(rect.Height()) * 0.5, steps);
0593   }
0594 
0595   template <typename T>
0596   inline Path<T> Ellipse(const Point<T>& center,
0597     double radiusX, double radiusY = 0, int steps = 0)
0598   {
0599     if (radiusX <= 0) return Path<T>();
0600     if (radiusY <= 0) radiusY = radiusX;
0601     if (steps <= 2)
0602       steps = static_cast<int>(PI * sqrt((radiusX + radiusY) / 2));
0603 
0604     double si = std::sin(2 * PI / steps);
0605     double co = std::cos(2 * PI / steps);
0606     double dx = co, dy = si;
0607     Path<T> result;
0608     result.reserve(steps);
0609     result.push_back(Point<T>(center.x + radiusX, static_cast<double>(center.y)));
0610     for (int i = 1; i < steps; ++i)
0611     {
0612       result.push_back(Point<T>(center.x + radiusX * dx, center.y + radiusY * dy));
0613       double x = dx * co - dy * si;
0614       dy = dy * co + dx * si;
0615       dx = x;
0616     }
0617     return result;
0618   }
0619 
0620   template <typename T>
0621   inline double PerpendicDistFromLineSqrd(const Point<T>& pt,
0622     const Point<T>& line1, const Point<T>& line2)
0623   {
0624     double a = static_cast<double>(pt.x - line1.x);
0625     double b = static_cast<double>(pt.y - line1.y);
0626     double c = static_cast<double>(line2.x - line1.x);
0627     double d = static_cast<double>(line2.y - line1.y);
0628     if (c == 0 && d == 0) return 0;
0629     return Sqr(a * d - c * b) / (c * c + d * d);
0630   }
0631 
0632   inline size_t GetNext(size_t current, size_t high, 
0633     const std::vector<bool>& flags)
0634   {
0635     ++current;
0636     while (current <= high && flags[current]) ++current;
0637     if (current <= high) return current;
0638     current = 0;
0639     while (flags[current]) ++current;
0640     return current;
0641   }
0642 
0643   inline size_t GetPrior(size_t current, size_t high, 
0644     const std::vector<bool>& flags)
0645   {
0646     if (current == 0) current = high;
0647     else --current;
0648     while (current > 0 && flags[current]) --current;
0649     if (!flags[current]) return current;
0650     current = high;
0651     while (flags[current]) --current;
0652     return current;
0653   }
0654 
0655   template <typename T>
0656   inline Path<T> SimplifyPath(const Path<T> path, 
0657     double epsilon, bool isClosedPath = true)
0658   {
0659     const size_t len = path.size(), high = len -1;
0660     const double epsSqr = Sqr(epsilon);
0661     if (len < 4) return Path<T>(path);
0662 
0663     std::vector<bool> flags(len);
0664     std::vector<double> distSqr(len);
0665     size_t prior = high, curr = 0, start, next, prior2, next2;
0666     if (isClosedPath)
0667     {
0668       distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]);
0669       distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]);
0670     }
0671     else 
0672     {
0673       distSqr[0] = MAX_DBL;
0674       distSqr[high] = MAX_DBL;
0675     }
0676     for (size_t i = 1; i < high; ++i)
0677       distSqr[i] = PerpendicDistFromLineSqrd(path[i], path[i - 1], path[i + 1]);
0678 
0679     for (;;)
0680     {
0681       if (distSqr[curr] > epsSqr)
0682       {
0683         start = curr;
0684         do
0685         {
0686           curr = GetNext(curr, high, flags);
0687         } while (curr != start && distSqr[curr] > epsSqr);
0688         if (curr == start) break;
0689       }
0690       
0691       prior = GetPrior(curr, high, flags);
0692       next = GetNext(curr, high, flags);
0693       if (next == prior) break;
0694 
0695       if (distSqr[next] < distSqr[curr])
0696       {
0697         flags[next] = true;
0698         next = GetNext(next, high, flags);
0699         next2 = GetNext(next, high, flags);
0700         distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
0701         if (next != high || isClosedPath)
0702           distSqr[next] = PerpendicDistFromLineSqrd(path[next], path[curr], path[next2]);
0703         curr = next;
0704       }
0705       else
0706       {
0707         flags[curr] = true;
0708         curr = next;
0709         next = GetNext(next, high, flags);
0710         prior2 = GetPrior(prior, high, flags);
0711         distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
0712         if (prior != 0 || isClosedPath)
0713           distSqr[prior] = PerpendicDistFromLineSqrd(path[prior], path[prior2], path[curr]);
0714       }
0715     }
0716     Path<T> result;
0717     result.reserve(len);
0718     for (typename Path<T>::size_type i = 0; i < len; ++i)
0719       if (!flags[i]) result.push_back(path[i]);
0720     return result;
0721   }
0722 
0723   template <typename T>
0724   inline Paths<T> SimplifyPaths(const Paths<T> paths, 
0725     double epsilon, bool isClosedPath = true)
0726   {
0727     Paths<T> result;
0728     result.reserve(paths.size());
0729     for (const auto& path : paths)
0730       result.push_back(SimplifyPath(path, epsilon, isClosedPath));
0731     return result;
0732   }
0733 
0734   template <typename T>
0735   inline void RDP(const Path<T> path, std::size_t begin,
0736     std::size_t end, double epsSqrd, std::vector<bool>& flags)
0737   {
0738     typename Path<T>::size_type idx = 0;
0739     double max_d = 0;
0740     while (end > begin && path[begin] == path[end]) flags[end--] = false;
0741     for (typename Path<T>::size_type i = begin + 1; i < end; ++i)
0742     {
0743       // PerpendicDistFromLineSqrd - avoids expensive Sqrt()
0744       double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]);
0745       if (d <= max_d) continue;
0746       max_d = d;
0747       idx = i;
0748     }
0749     if (max_d <= epsSqrd) return;
0750     flags[idx] = true;
0751     if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags);
0752     if (idx < end - 1) RDP(path, idx, end, epsSqrd, flags);
0753   }
0754 
0755   template <typename T>
0756   inline Path<T> RamerDouglasPeucker(const Path<T>& path, double epsilon)
0757   {
0758     const typename Path<T>::size_type len = path.size();
0759     if (len < 5) return Path<T>(path);
0760     std::vector<bool> flags(len);
0761     flags[0] = true;
0762     flags[len - 1] = true;
0763     RDP(path, 0, len - 1, Sqr(epsilon), flags);
0764     Path<T> result;
0765     result.reserve(len);
0766     for (typename Path<T>::size_type i = 0; i < len; ++i)
0767       if (flags[i])
0768         result.push_back(path[i]);
0769     return result;
0770   }
0771 
0772   template <typename T>
0773   inline Paths<T> RamerDouglasPeucker(const Paths<T>& paths, double epsilon)
0774   {
0775     Paths<T> result;
0776     result.reserve(paths.size());
0777     std::transform(paths.begin(), paths.end(), back_inserter(result),
0778       [epsilon](const auto& path)
0779       { return RamerDouglasPeucker<T>(path, epsilon); });
0780     return result;
0781   }
0782 
0783 }  // end Clipper2Lib namespace
0784 
0785 #endif  // CLIPPER_H