File indexing completed on 2024-05-12 03:52:05

0001 /*******************************************************************************
0002 * Author    :  Angus Johnson                                                   *
0003 * Date      :  26 July 2023                                                    *
0004 * Website   :  http://www.angusj.com                                           *
0005 * Copyright :  Angus Johnson 2010-2023                                         *
0006 * Purpose   :  Core Clipper Library structures and functions                   *
0007 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
0008 *******************************************************************************/
0009 
0010 #ifndef CLIPPER_CORE_H
0011 #define CLIPPER_CORE_H
0012 
0013 #include <cstdint>
0014 #include <cstdlib>
0015 #include <cmath>
0016 #include <vector>
0017 #include <string>
0018 #include <iostream>
0019 #include <algorithm>
0020 #include <climits>
0021 #include <numeric>
0022 
0023 namespace Clipper2Lib
0024 {
0025 
0026 #if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
0027 
0028   class Clipper2Exception : public std::exception {
0029   public:
0030     explicit Clipper2Exception(const char* description) :
0031       m_descr(description) {}
0032     virtual const char* what() const throw() override { return m_descr.c_str(); }
0033   private:
0034     std::string m_descr;
0035   };
0036 
0037   static const char* precision_error =
0038     "Precision exceeds the permitted range";
0039   static const char* range_error =
0040     "Values exceed permitted range";
0041   static const char* scale_error =
0042     "Invalid scale (either 0 or too large)";
0043   static const char* non_pair_error =
0044     "There must be 2 values for each coordinate";
0045 #endif
0046 
0047   // error codes (2^n)
0048   const int precision_error_i   = 1; // non-fatal
0049   const int scale_error_i       = 2; // non-fatal 
0050   const int non_pair_error_i    = 4; // non-fatal 
0051   const int range_error_i = 64;
0052 
0053 #ifndef PI
0054   static const double PI = 3.141592653589793238;
0055 #endif
0056   static const int MAX_DECIMAL_PRECISION = 8; // see https://github.com/AngusJohnson/Clipper2/discussions/564
0057   static const int64_t MAX_COORD = INT64_MAX >> 2;
0058   static const int64_t MIN_COORD = -MAX_COORD;
0059   static const int64_t INVALID = INT64_MAX;
0060   const double max_coord = static_cast<double>(MAX_COORD);
0061   const double min_coord = static_cast<double>(MIN_COORD);
0062 
0063   static const double MAX_DBL = (std::numeric_limits<double>::max)();
0064 
0065   static void DoError(int error_code)
0066   {
0067 #if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
0068     switch (error_code)
0069     {
0070     case precision_error_i:
0071       throw Clipper2Exception(precision_error);
0072     case scale_error_i:
0073       throw Clipper2Exception(scale_error);
0074     case non_pair_error_i:
0075       throw Clipper2Exception(non_pair_error);
0076     case range_error_i:
0077       throw Clipper2Exception(range_error);
0078     }
0079 #else
0080     ++error_code; // only to stop compiler warning
0081 #endif
0082   }
0083 
0084   //By far the most widely used filling rules for polygons are EvenOdd
0085   //and NonZero, sometimes called Alternate and Winding respectively.
0086   //https://en.wikipedia.org/wiki/Nonzero-rule
0087   enum class FillRule { EvenOdd, NonZero, Positive, Negative };
0088 
0089   // Point ------------------------------------------------------------------------
0090 
0091   template <typename T>
0092   struct Point {
0093     T x;
0094     T y;
0095 #ifdef USINGZ
0096     int64_t z;
0097 
0098     template <typename T2>
0099     inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0)
0100     {
0101       if constexpr (std::numeric_limits<T>::is_integer &&
0102         !std::numeric_limits<T2>::is_integer)
0103       {
0104         x = static_cast<T>(std::round(x_));
0105         y = static_cast<T>(std::round(y_));
0106         z = z_;
0107       }
0108       else
0109       {
0110         x = static_cast<T>(x_);
0111         y = static_cast<T>(y_);
0112         z = z_;
0113       }
0114     }
0115 
0116     explicit Point() : x(0), y(0), z(0) {};
0117 
0118     template <typename T2>
0119     Point(const T2 x_, const T2 y_, const int64_t z_ = 0)
0120     {
0121       Init(x_, y_);
0122       z = z_;
0123     }
0124 
0125     template <typename T2>
0126     explicit Point<T>(const Point<T2>& p)
0127     {
0128       Init(p.x, p.y, p.z);
0129     }
0130 
0131     Point operator * (const double scale) const
0132     {
0133       return Point(x * scale, y * scale, z);
0134     }
0135 
0136     void SetZ(const int64_t z_value) { z = z_value; }
0137 
0138     friend std::ostream& operator<<(std::ostream& os, const Point& point)
0139     {
0140       os << point.x << "," << point.y << "," << point.z << " ";
0141       return os;
0142     }
0143 
0144 #else
0145 
0146     template <typename T2>
0147     inline void Init(const T2 x_ = 0, const T2 y_ = 0)
0148     {
0149       if constexpr (std::numeric_limits<T>::is_integer &&
0150         !std::numeric_limits<T2>::is_integer)
0151       {
0152         x = static_cast<T>(std::round(x_));
0153         y = static_cast<T>(std::round(y_));
0154       }
0155       else
0156       {
0157         x = static_cast<T>(x_);
0158         y = static_cast<T>(y_);
0159       }
0160     }
0161 
0162     explicit Point() : x(0), y(0) {};
0163 
0164     template <typename T2>
0165     Point(const T2 x_, const T2 y_) { Init(x_, y_); }
0166 
0167     template <typename T2>
0168     explicit Point<T>(const Point<T2>& p) { Init(p.x, p.y); }
0169 
0170     Point operator * (const double scale) const
0171     {
0172       return Point(x * scale, y * scale);
0173     }
0174 
0175     friend std::ostream& operator<<(std::ostream& os, const Point& point)
0176     {
0177       os << point.x << "," << point.y << " ";
0178       return os;
0179     }
0180 #endif
0181 
0182     friend bool operator==(const Point& a, const Point& b)
0183     {
0184       return a.x == b.x && a.y == b.y;
0185     }
0186 
0187     friend bool operator!=(const Point& a, const Point& b)
0188     {
0189       return !(a == b);
0190     }
0191 
0192     inline Point<T> operator-() const
0193     {
0194       return Point<T>(-x, -y);
0195     }
0196 
0197     inline Point operator+(const Point& b) const
0198     {
0199       return Point(x + b.x, y + b.y);
0200     }
0201 
0202     inline Point operator-(const Point& b) const
0203     {
0204       return Point(x - b.x, y - b.y);
0205     }
0206 
0207     inline void Negate() { x = -x; y = -y; }
0208 
0209   };
0210 
0211   //nb: using 'using' here (instead of typedef) as they can be used in templates
0212   using Point64 = Point<int64_t>;
0213   using PointD = Point<double>;
0214 
0215   template <typename T>
0216   using Path = std::vector<Point<T>>;
0217   template <typename T>
0218   using Paths = std::vector<Path<T>>;
0219 
0220   using Path64 = Path<int64_t>;
0221   using PathD = Path<double>;
0222   using Paths64 = std::vector< Path64>;
0223   using PathsD = std::vector< PathD>;
0224 
0225   // Rect ------------------------------------------------------------------------
0226 
0227   template <typename T>
0228   struct Rect;
0229 
0230   using Rect64 = Rect<int64_t>;
0231   using RectD = Rect<double>;
0232 
0233   template <typename T>
0234   struct Rect {
0235     T left;
0236     T top;
0237     T right;
0238     T bottom;
0239 
0240     Rect() :
0241       left(0),
0242       top(0),
0243       right(0),
0244       bottom(0) {}
0245 
0246     Rect(T l, T t, T r, T b) :
0247       left(l),
0248       top(t),
0249       right(r),
0250       bottom(b) {}
0251 
0252     Rect(bool is_valid)
0253     {
0254       if (is_valid)
0255       {
0256         left = right = top = bottom = 0;
0257       }
0258       else
0259       {
0260         left = top = (std::numeric_limits<T>::max)();
0261         right = bottom = -(std::numeric_limits<int64_t>::max)();
0262       }
0263     }
0264 
0265     T Width() const { return right - left; }
0266     T Height() const { return bottom - top; }
0267     void Width(T width) { right = left + width; }
0268     void Height(T height) { bottom = top + height; }
0269 
0270     Point<T> MidPoint() const
0271     {
0272       return Point<T>((left + right) / 2, (top + bottom) / 2);
0273     }
0274 
0275     Path<T> AsPath() const
0276     {
0277       Path<T> result;
0278       result.reserve(4);
0279       result.push_back(Point<T>(left, top));
0280       result.push_back(Point<T>(right, top));
0281       result.push_back(Point<T>(right, bottom));
0282       result.push_back(Point<T>(left, bottom));
0283       return result;
0284     }
0285 
0286     bool Contains(const Point<T>& pt) const
0287     {
0288       return pt.x > left && pt.x < right&& pt.y > top && pt.y < bottom;
0289     }
0290 
0291     bool Contains(const Rect<T>& rec) const
0292     {
0293       return rec.left >= left && rec.right <= right &&
0294         rec.top >= top && rec.bottom <= bottom;
0295     }
0296 
0297     void Scale(double scale) {
0298       left *= scale;
0299       top *= scale;
0300       right *= scale;
0301       bottom *= scale;
0302     }
0303 
0304     bool IsEmpty() const { return bottom <= top || right <= left; };
0305 
0306     bool Intersects(const Rect<T>& rec) const
0307     {
0308       return ((std::max)(left, rec.left) <= (std::min)(right, rec.right)) &&
0309         ((std::max)(top, rec.top) <= (std::min)(bottom, rec.bottom));
0310     };
0311 
0312     friend std::ostream& operator<<(std::ostream& os, const Rect<T>& rect) {
0313       os << "("
0314         << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom
0315         << ")";
0316       return os;
0317     }
0318   };
0319 
0320   template <typename T1, typename T2>
0321   inline Rect<T1> ScaleRect(const Rect<T2>& rect, double scale)
0322   {
0323     Rect<T1> result;
0324 
0325     if constexpr (std::numeric_limits<T1>::is_integer &&
0326       !std::numeric_limits<T2>::is_integer)
0327     {
0328       result.left = static_cast<T1>(std::round(rect.left * scale));
0329       result.top = static_cast<T1>(std::round(rect.top * scale));
0330       result.right = static_cast<T1>(std::round(rect.right * scale));
0331       result.bottom = static_cast<T1>(std::round(rect.bottom * scale));
0332     }
0333     else
0334     {
0335       result.left = rect.left * scale;
0336       result.top = rect.top * scale;
0337       result.right = rect.right * scale;
0338       result.bottom = rect.bottom * scale;
0339     }
0340     return result;
0341   }
0342 
0343   static const Rect64 MaxInvalidRect64 = Rect64(
0344     INT64_MAX, INT64_MAX, INT64_MIN, INT64_MIN);
0345   static const RectD MaxInvalidRectD = RectD(
0346     MAX_DBL, MAX_DBL, -MAX_DBL, -MAX_DBL);
0347 
0348   template <typename T>
0349   Rect<T> GetBounds(const Path<T>& path)
0350   {
0351     auto xmin = (std::numeric_limits<T>::max)();
0352     auto ymin = (std::numeric_limits<T>::max)();
0353     auto xmax = std::numeric_limits<T>::lowest();
0354     auto ymax = std::numeric_limits<T>::lowest();
0355     for (const auto& p : path)
0356     {
0357       if (p.x < xmin) xmin = p.x;
0358       if (p.x > xmax) xmax = p.x;
0359       if (p.y < ymin) ymin = p.y;
0360       if (p.y > ymax) ymax = p.y;
0361     }
0362     return Rect<T>(xmin, ymin, xmax, ymax);
0363   }
0364 
0365   template <typename T>
0366   Rect<T> GetBounds(const Paths<T>& paths)
0367   {
0368     auto xmin = (std::numeric_limits<T>::max)();
0369     auto ymin = (std::numeric_limits<T>::max)();
0370     auto xmax = std::numeric_limits<T>::lowest();
0371     auto ymax = std::numeric_limits<T>::lowest();
0372     for (const Path<T>& path : paths)
0373       for (const Point<T>& p : path)
0374       {
0375       if (p.x < xmin) xmin = p.x;
0376       if (p.x > xmax) xmax = p.x;
0377       if (p.y < ymin) ymin = p.y;
0378       if (p.y > ymax) ymax = p.y;
0379       }
0380     return Rect<T>(xmin, ymin, xmax, ymax);
0381   }
0382 
0383   template <typename T>
0384   std::ostream& operator << (std::ostream& outstream, const Path<T>& path)
0385   {
0386     if (!path.empty())
0387     {
0388       auto pt = path.cbegin(), last = path.cend() - 1;
0389       while (pt != last)
0390         outstream << *pt++ << ", ";
0391       outstream << *last << std::endl;
0392     }
0393     return outstream;
0394   }
0395 
0396   template <typename T>
0397   std::ostream& operator << (std::ostream& outstream, const Paths<T>& paths)
0398   {
0399     for (auto p : paths)
0400       outstream << p;
0401     return outstream;
0402   }
0403 
0404 
0405   template <typename T1, typename T2>
0406   inline Path<T1> ScalePath(const Path<T2>& path, 
0407     double scale_x, double scale_y, int& error_code)
0408   {
0409     Path<T1> result;
0410     if (scale_x == 0 || scale_y == 0)
0411     {
0412       error_code |= scale_error_i;
0413       DoError(scale_error_i);
0414       // if no exception, treat as non-fatal error
0415       if (scale_x == 0) scale_x = 1.0;
0416       if (scale_y == 0) scale_y = 1.0;
0417     }
0418 
0419     result.reserve(path.size());
0420 #ifdef USINGZ
0421     std::transform(path.begin(), path.end(), back_inserter(result),
0422       [scale_x, scale_y](const auto& pt) 
0423       { return Point<T1>(pt.x * scale_x, pt.y * scale_y, pt.z); });
0424 #else
0425     std::transform(path.begin(), path.end(), back_inserter(result),
0426       [scale_x, scale_y](const auto& pt) 
0427       { return Point<T1>(pt.x * scale_x, pt.y * scale_y); });
0428 #endif
0429     return result;
0430   }
0431 
0432   template <typename T1, typename T2>
0433   inline Path<T1> ScalePath(const Path<T2>& path,
0434     double scale, int& error_code)
0435   {
0436     return ScalePath<T1, T2>(path, scale, scale, error_code);
0437   }
0438 
0439   template <typename T1, typename T2>
0440   inline Paths<T1> ScalePaths(const Paths<T2>& paths, 
0441     double scale_x, double scale_y, int& error_code)
0442   {
0443     Paths<T1> result;
0444 
0445     if constexpr (std::numeric_limits<T1>::is_integer &&
0446       !std::numeric_limits<T2>::is_integer)
0447     {
0448       RectD r = GetBounds(paths);
0449       if ((r.left * scale_x) < min_coord ||
0450         (r.right * scale_x) > max_coord ||
0451         (r.top * scale_y) < min_coord ||
0452         (r.bottom * scale_y) > max_coord)
0453       { 
0454         error_code |= range_error_i;
0455         DoError(range_error_i);
0456         return result; // empty path
0457       }
0458     }
0459 
0460     result.reserve(paths.size());
0461     std::transform(paths.begin(), paths.end(), back_inserter(result),
0462       [=, &error_code](const auto& path)
0463       { return ScalePath<T1, T2>(path, scale_x, scale_y, error_code); });
0464     return result;
0465   }
0466 
0467   template <typename T1, typename T2>
0468   inline Paths<T1> ScalePaths(const Paths<T2>& paths, 
0469     double scale, int& error_code)
0470   {
0471     return ScalePaths<T1, T2>(paths, scale, scale, error_code);
0472   }
0473 
0474   template <typename T1, typename T2>
0475   inline Path<T1> TransformPath(const Path<T2>& path)
0476   {
0477     Path<T1> result;
0478     result.reserve(path.size());
0479     std::transform(path.cbegin(), path.cend(), std::back_inserter(result),
0480       [](const Point<T2>& pt) {return Point<T1>(pt); });
0481     return result;
0482   }
0483 
0484   template <typename T1, typename T2>
0485   inline Paths<T1> TransformPaths(const Paths<T2>& paths)
0486   {
0487     Paths<T1> result;
0488     std::transform(paths.cbegin(), paths.cend(), std::back_inserter(result),
0489       [](const Path<T2>& path) {return TransformPath<T1, T2>(path); });
0490     return result;
0491   }
0492 
0493   inline PathD Path64ToPathD(const Path64& path)
0494   {
0495     return TransformPath<double, int64_t>(path);
0496   }
0497 
0498   inline PathsD Paths64ToPathsD(const Paths64& paths)
0499   {
0500     return TransformPaths<double, int64_t>(paths);
0501   }
0502 
0503   inline Path64 PathDToPath64(const PathD& path)
0504   {
0505     return TransformPath<int64_t, double>(path);
0506   }
0507 
0508   inline Paths64 PathsDToPaths64(const PathsD& paths)
0509   {
0510     return TransformPaths<int64_t, double>(paths);
0511   }
0512 
0513   template<typename T>
0514   inline double Sqr(T val)
0515   {
0516     return static_cast<double>(val) * static_cast<double>(val);
0517   }
0518 
0519   template<typename T>
0520   inline bool NearEqual(const Point<T>& p1,
0521     const Point<T>& p2, double max_dist_sqrd)
0522   {
0523     return Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y) < max_dist_sqrd;
0524   }
0525 
0526   template<typename T>
0527   inline Path<T> StripNearEqual(const Path<T>& path,
0528     double max_dist_sqrd, bool is_closed_path)
0529   {
0530     if (path.size() == 0) return Path<T>();
0531     Path<T> result;
0532     result.reserve(path.size());
0533     typename Path<T>::const_iterator path_iter = path.cbegin();
0534     Point<T> first_pt = *path_iter++, last_pt = first_pt;
0535     result.push_back(first_pt);
0536     for (; path_iter != path.cend(); ++path_iter)
0537     {
0538       if (!NearEqual(*path_iter, last_pt, max_dist_sqrd))
0539       {
0540         last_pt = *path_iter;
0541         result.push_back(last_pt);
0542       }
0543     }
0544     if (!is_closed_path) return result;
0545     while (result.size() > 1 &&
0546       NearEqual(result.back(), first_pt, max_dist_sqrd)) result.pop_back();
0547     return result;
0548   }
0549 
0550   template<typename T>
0551   inline Paths<T> StripNearEqual(const Paths<T>& paths,
0552     double max_dist_sqrd, bool is_closed_path)
0553   {
0554     Paths<T> result;
0555     result.reserve(paths.size());
0556     for (typename Paths<T>::const_iterator paths_citer = paths.cbegin();
0557       paths_citer != paths.cend(); ++paths_citer)
0558     {
0559       result.push_back(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path));
0560     }
0561     return result;
0562   }
0563 
0564   template<typename T>
0565   inline void StripDuplicates( Path<T>& path, bool is_closed_path)
0566   {
0567     //https://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector#:~:text=Let%27s%20compare%20three%20approaches%3A
0568     path.erase(std::unique(path.begin(), path.end()),path.end());
0569     if (is_closed_path)
0570       while (path.size() > 1 && path.back() == path.front()) path.pop_back();
0571   }
0572 
0573   template<typename T>
0574   inline void StripDuplicates( Paths<T>& paths, bool is_closed_path)
0575   {
0576     for (typename Paths<T>::iterator paths_citer = paths.begin();
0577       paths_citer != paths.end(); ++paths_citer)
0578     {
0579       StripDuplicates(*paths_citer, is_closed_path);
0580     }
0581   }
0582 
0583   // Miscellaneous ------------------------------------------------------------
0584 
0585   inline void CheckPrecision(int& precision, int& error_code)
0586   {
0587     if (precision >= -MAX_DECIMAL_PRECISION && precision <= MAX_DECIMAL_PRECISION) return;
0588     error_code |= precision_error_i; // non-fatal error
0589     DoError(precision_error_i);      // does nothing unless exceptions enabled
0590     precision = precision > 0 ? MAX_DECIMAL_PRECISION : -MAX_DECIMAL_PRECISION;
0591   }
0592 
0593   inline void CheckPrecision(int& precision)
0594   {
0595     int error_code = 0;
0596     CheckPrecision(precision, error_code);
0597   }
0598 
0599   template <typename T>
0600   inline double CrossProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
0601     return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.y -
0602       pt2.y) - static_cast<double>(pt2.y - pt1.y) * static_cast<double>(pt3.x - pt2.x));
0603   }
0604 
0605   template <typename T>
0606   inline double CrossProduct(const Point<T>& vec1, const Point<T>& vec2)
0607   {
0608     return static_cast<double>(vec1.y * vec2.x) - static_cast<double>(vec2.y * vec1.x);
0609   }
0610 
0611   template <typename T>
0612   inline double DotProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
0613     return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.x - pt2.x) +
0614       static_cast<double>(pt2.y - pt1.y) * static_cast<double>(pt3.y - pt2.y));
0615   }
0616 
0617   template <typename T>
0618   inline double DotProduct(const Point<T>& vec1, const Point<T>& vec2)
0619   {
0620     return static_cast<double>(vec1.x * vec2.x) + static_cast<double>(vec1.y * vec2.y);
0621   }
0622 
0623   template <typename T>
0624   inline double DistanceSqr(const Point<T> pt1, const Point<T> pt2)
0625   {
0626     return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y);
0627   }
0628 
0629   template <typename T>
0630   inline double DistanceFromLineSqrd(const Point<T>& pt, const Point<T>& ln1, const Point<T>& ln2)
0631   {
0632     //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
0633     //see http://en.wikipedia.org/wiki/Perpendicular_distance
0634     double A = static_cast<double>(ln1.y - ln2.y);
0635     double B = static_cast<double>(ln2.x - ln1.x);
0636     double C = A * ln1.x + B * ln1.y;
0637     C = A * pt.x + B * pt.y - C;
0638     return (C * C) / (A * A + B * B);
0639   }
0640 
0641   template <typename T>
0642   inline double Area(const Path<T>& path)
0643   {
0644     size_t cnt = path.size();
0645     if (cnt < 3) return 0.0;
0646     double a = 0.0;
0647     typename Path<T>::const_iterator it1, it2 = path.cend() - 1, stop = it2;
0648     if (!(cnt & 1)) ++stop;
0649     for (it1 = path.cbegin(); it1 != stop;)
0650     {
0651       a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
0652       it2 = it1 + 1;
0653       a += static_cast<double>(it1->y + it2->y) * (it1->x - it2->x);
0654       it1 += 2;
0655     }
0656     if (cnt & 1)
0657       a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
0658     return a * 0.5;
0659   }
0660 
0661   template <typename T>
0662   inline double Area(const Paths<T>& paths)
0663   {
0664     double a = 0.0;
0665     for (typename Paths<T>::const_iterator paths_iter = paths.cbegin();
0666       paths_iter != paths.cend(); ++paths_iter)
0667     {
0668       a += Area<T>(*paths_iter);
0669     }
0670     return a;
0671   }
0672 
0673   template <typename T>
0674   inline bool IsPositive(const Path<T>& poly)
0675   {
0676     // A curve has positive orientation [and area] if a region 'R' 
0677     // is on the left when traveling around the outside of 'R'.
0678     //https://mathworld.wolfram.com/CurveOrientation.html
0679     //nb: This statement is premised on using Cartesian coordinates
0680     return Area<T>(poly) >= 0;
0681   }
0682   
0683   inline bool GetIntersectPoint(const Point64& ln1a, const Point64& ln1b,
0684     const Point64& ln2a, const Point64& ln2b, Point64& ip)
0685   {  
0686     // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
0687     double dx1 = static_cast<double>(ln1b.x - ln1a.x);
0688     double dy1 = static_cast<double>(ln1b.y - ln1a.y);
0689     double dx2 = static_cast<double>(ln2b.x - ln2a.x);
0690     double dy2 = static_cast<double>(ln2b.y - ln2a.y);
0691 
0692     double det = dy1 * dx2 - dy2 * dx1;
0693     if (det == 0.0) return false;
0694     double t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det;
0695     if (t <= 0.0) ip = ln1a;        // ?? check further (see also #568)
0696     else if (t >= 1.0) ip = ln1b;   // ?? check further
0697     else
0698     {
0699       ip.x = static_cast<int64_t>(ln1a.x + t * dx1);
0700       ip.y = static_cast<int64_t>(ln1a.y + t * dy1);
0701     }
0702     return true;
0703   }
0704 
0705   inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
0706     const Point64& seg2a, const Point64& seg2b, bool inclusive = false)
0707   {
0708     if (inclusive)
0709     {
0710       double res1 = CrossProduct(seg1a, seg2a, seg2b);
0711       double res2 = CrossProduct(seg1b, seg2a, seg2b);
0712       if (res1 * res2 > 0) return false;
0713       double res3 = CrossProduct(seg2a, seg1a, seg1b);
0714       double res4 = CrossProduct(seg2b, seg1a, seg1b);
0715       if (res3 * res4 > 0) return false;
0716       return (res1 || res2 || res3 || res4); // ensures not collinear
0717     }
0718     else {
0719       return (CrossProduct(seg1a, seg2a, seg2b) *
0720         CrossProduct(seg1b, seg2a, seg2b) < 0) &&
0721         (CrossProduct(seg2a, seg1a, seg1b) *
0722           CrossProduct(seg2b, seg1a, seg1b) < 0);
0723     }
0724   }
0725 
0726   inline Point64 GetClosestPointOnSegment(const Point64& offPt,
0727     const Point64& seg1, const Point64& seg2)
0728   {
0729     if (seg1.x == seg2.x && seg1.y == seg2.y) return seg1;
0730     double dx = static_cast<double>(seg2.x - seg1.x);
0731     double dy = static_cast<double>(seg2.y - seg1.y);
0732     double q =
0733       (static_cast<double>(offPt.x - seg1.x) * dx +
0734         static_cast<double>(offPt.y - seg1.y) * dy) /
0735       (Sqr(dx) + Sqr(dy));
0736     if (q < 0) q = 0; else if (q > 1) q = 1;
0737     return Point64(
0738       seg1.x + static_cast<int64_t>(nearbyint(q * dx)),
0739       seg1.y + static_cast<int64_t>(nearbyint(q * dy)));
0740   }
0741 
0742   enum class PointInPolygonResult { IsOn, IsInside, IsOutside };
0743 
0744   template <typename T>
0745   inline PointInPolygonResult PointInPolygon(const Point<T>& pt, const Path<T>& polygon)
0746   {
0747     if (polygon.size() < 3)
0748       return PointInPolygonResult::IsOutside;
0749 
0750     int val = 0;
0751     typename Path<T>::const_iterator cbegin = polygon.cbegin(), first = cbegin, curr, prev;
0752     typename Path<T>::const_iterator cend = polygon.cend();
0753 
0754     while (first != cend && first->y == pt.y) ++first;
0755     if (first == cend) // not a proper polygon
0756       return PointInPolygonResult::IsOutside;
0757 
0758     bool is_above = first->y < pt.y, starting_above = is_above;
0759     curr = first +1; 
0760     while (true)
0761     {
0762       if (curr == cend)
0763       {
0764         if (cend == first || first == cbegin) break;
0765         cend = first;
0766         curr = cbegin;
0767       }
0768       
0769       if (is_above)
0770       {
0771         while (curr != cend && curr->y < pt.y) ++curr;
0772         if (curr == cend) continue;
0773       }
0774       else
0775       {
0776         while (curr != cend && curr->y > pt.y) ++curr;
0777         if (curr == cend) continue;
0778       }
0779 
0780       if (curr == cbegin) 
0781         prev = polygon.cend() - 1; //nb: NOT cend (since might equal first)
0782       else  
0783         prev = curr - 1;
0784 
0785       if (curr->y == pt.y)
0786       {
0787         if (curr->x == pt.x || 
0788           (curr->y == prev->y &&
0789             ((pt.x < prev->x) != (pt.x < curr->x))))
0790               return PointInPolygonResult::IsOn;
0791         ++curr;
0792         if (curr == first) break;
0793         continue;
0794       }
0795 
0796       if (pt.x < curr->x && pt.x < prev->x)
0797       {
0798         // we're only interested in edges crossing on the left
0799       }
0800       else if (pt.x > prev->x && pt.x > curr->x)
0801         val = 1 - val; // toggle val
0802       else
0803       {
0804         double d = CrossProduct(*prev, *curr, pt);
0805         if (d == 0) return PointInPolygonResult::IsOn;
0806         if ((d < 0) == is_above) val = 1 - val;
0807       }
0808       is_above = !is_above;
0809       ++curr;
0810     }
0811     
0812     if (is_above != starting_above)
0813     {
0814       cend = polygon.cend();
0815       if (curr == cend) curr = cbegin;
0816       if (curr == cbegin) prev = cend - 1;
0817       else prev = curr - 1;
0818       double d = CrossProduct(*prev, *curr, pt);
0819       if (d == 0) return PointInPolygonResult::IsOn;
0820       if ((d < 0) == is_above) val = 1 - val;
0821     }
0822 
0823     return (val == 0) ?
0824       PointInPolygonResult::IsOutside :
0825       PointInPolygonResult::IsInside;
0826   }
0827 
0828 }  // namespace
0829 
0830 #endif  // CLIPPER_CORE_H