File indexing completed on 2025-04-20 03:36:27
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