clipper2 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +165 -0
  4. data/examples/larb_transform.rb +17 -0
  5. data/examples/rugl_mesh.rb +33 -0
  6. data/examples/svg_boolean.rb +28 -0
  7. data/ext/clipper2/CMakeLists.txt +27 -0
  8. data/ext/clipper2/clipper2_native.cpp +2 -0
  9. data/ext/clipper2/extconf.rb +27 -0
  10. data/ext/clipper2/generate_bindings.rb +73 -0
  11. data/ext/clipper2/memory_check.cpp +48 -0
  12. data/ext/clipper2/vendor/LICENSE +23 -0
  13. data/ext/clipper2/vendor/UPSTREAM.md +11 -0
  14. data/ext/clipper2/vendor/include/clipper2/clipper.core.h +1159 -0
  15. data/ext/clipper2/vendor/include/clipper2/clipper.engine.h +635 -0
  16. data/ext/clipper2/vendor/include/clipper2/clipper.export.h +851 -0
  17. data/ext/clipper2/vendor/include/clipper2/clipper.h +796 -0
  18. data/ext/clipper2/vendor/include/clipper2/clipper.minkowski.h +117 -0
  19. data/ext/clipper2/vendor/include/clipper2/clipper.offset.h +125 -0
  20. data/ext/clipper2/vendor/include/clipper2/clipper.rectclip.h +80 -0
  21. data/ext/clipper2/vendor/include/clipper2/clipper.triangulation.h +27 -0
  22. data/ext/clipper2/vendor/include/clipper2/clipper.version.h +6 -0
  23. data/ext/clipper2/vendor/src/clipper.engine.cpp +3152 -0
  24. data/ext/clipper2/vendor/src/clipper.offset.cpp +661 -0
  25. data/ext/clipper2/vendor/src/clipper.rectclip.cpp +1027 -0
  26. data/ext/clipper2/vendor/src/clipper.triangulation.cpp +1221 -0
  27. data/lib/clipper2/api.rb +45 -0
  28. data/lib/clipper2/c_paths64.rb +80 -0
  29. data/lib/clipper2/earcut/engine.rb +191 -0
  30. data/lib/clipper2/earcut/geometry.rb +100 -0
  31. data/lib/clipper2/earcut/node.rb +18 -0
  32. data/lib/clipper2/earcut/ring.rb +94 -0
  33. data/lib/clipper2/earcut.rb +18 -0
  34. data/lib/clipper2/errors.rb +19 -0
  35. data/lib/clipper2/generated.rb +62 -0
  36. data/lib/clipper2/native.rb +38 -0
  37. data/lib/clipper2/native_memory.rb +64 -0
  38. data/lib/clipper2/operations.rb +206 -0
  39. data/lib/clipper2/path.rb +171 -0
  40. data/lib/clipper2/path_simplifier.rb +61 -0
  41. data/lib/clipper2/paths.rb +130 -0
  42. data/lib/clipper2/quantizer.rb +48 -0
  43. data/lib/clipper2/triangulation.rb +103 -0
  44. data/lib/clipper2/version.rb +5 -0
  45. data/lib/clipper2.rb +34 -0
  46. data/licenses/CLIPPER2-BSL-1.0.txt +23 -0
  47. data/licenses/EARCUT-ISC.txt +15 -0
  48. metadata +113 -0
@@ -0,0 +1,1159 @@
1
+ /*******************************************************************************
2
+ * Author : Angus Johnson *
3
+ * Date : 12 October 2025 *
4
+ * Website : https://www.angusj.com *
5
+ * Copyright : Angus Johnson 2010-2025 *
6
+ * Purpose : Core Clipper Library structures and functions *
7
+ * License : https://www.boost.org/LICENSE_1_0.txt *
8
+ *******************************************************************************/
9
+
10
+ #ifndef CLIPPER_CORE_H
11
+ #define CLIPPER_CORE_H
12
+
13
+ #include "clipper2/clipper.version.h"
14
+ #include <cstdint>
15
+ #include <vector>
16
+ #include <string>
17
+ #include <iostream>
18
+ #include <algorithm>
19
+ #include <numeric>
20
+ #include <cmath>
21
+
22
+ namespace Clipper2Lib
23
+ {
24
+
25
+ #if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
26
+
27
+ class Clipper2Exception : public std::exception {
28
+ public:
29
+ explicit Clipper2Exception(const char* description) :
30
+ m_descr(description) {}
31
+ virtual const char* what() const noexcept override { return m_descr.c_str(); }
32
+ private:
33
+ std::string m_descr;
34
+ };
35
+
36
+ static const char* precision_error =
37
+ "Precision exceeds the permitted range";
38
+ static const char* range_error =
39
+ "Values exceed permitted range";
40
+ static const char* scale_error =
41
+ "Invalid scale (either 0 or too large)";
42
+ static const char* non_pair_error =
43
+ "There must be 2 values for each coordinate";
44
+ static const char* undefined_error =
45
+ "There is an undefined error in Clipper2";
46
+ #endif
47
+
48
+ // error codes (2^n)
49
+ const int precision_error_i = 1; // non-fatal
50
+ const int scale_error_i = 2; // non-fatal
51
+ const int non_pair_error_i = 4; // non-fatal
52
+ const int undefined_error_i = 32; // fatal
53
+ const int range_error_i = 64;
54
+
55
+ #ifndef PI
56
+ static const double PI = 3.141592653589793238;
57
+ #endif
58
+
59
+ #ifdef CLIPPER2_MAX_DECIMAL_PRECISION
60
+ const int CLIPPER2_MAX_DEC_PRECISION = CLIPPER2_MAX_DECIMAL_PRECISION;
61
+ #else
62
+ const int CLIPPER2_MAX_DEC_PRECISION = 8; // see Discussions #564
63
+ #endif
64
+
65
+ static const int64_t MAX_COORD = INT64_MAX >> 2;
66
+ static const int64_t MIN_COORD = -MAX_COORD;
67
+ static const int64_t INVALID = INT64_MAX;
68
+ const double max_coord = static_cast<double>(MAX_COORD);
69
+ const double min_coord = static_cast<double>(MIN_COORD);
70
+
71
+ static const double MAX_DBL = (std::numeric_limits<double>::max)();
72
+
73
+ static void DoError([[maybe_unused]] int error_code)
74
+ {
75
+ #if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
76
+ switch (error_code)
77
+ {
78
+ case precision_error_i:
79
+ throw Clipper2Exception(precision_error);
80
+ case scale_error_i:
81
+ throw Clipper2Exception(scale_error);
82
+ case non_pair_error_i:
83
+ throw Clipper2Exception(non_pair_error);
84
+ case undefined_error_i:
85
+ throw Clipper2Exception(undefined_error);
86
+ case range_error_i:
87
+ throw Clipper2Exception(range_error);
88
+ // Should never happen, but adding this to stop a compiler warning
89
+ default:
90
+ throw Clipper2Exception("Unknown error");
91
+ }
92
+ #else
93
+ ++error_code; // only to stop compiler warning
94
+ #endif
95
+ }
96
+
97
+ // can we call std::round on T? (default false) (#824)
98
+ template <typename T, typename = void>
99
+ struct is_round_invocable : std::false_type {};
100
+
101
+ template <typename T>
102
+ struct is_round_invocable<T, std::void_t<decltype(std::round(std::declval<T>()))>> : std::true_type {};
103
+
104
+
105
+ //By far the most widely used filling rules for polygons are EvenOdd
106
+ //and NonZero, sometimes called Alternate and Winding respectively.
107
+ //https://en.wikipedia.org/wiki/Nonzero-rule
108
+ enum class FillRule { EvenOdd, NonZero, Positive, Negative };
109
+
110
+ #ifdef USINGZ
111
+ using z_type = int64_t;
112
+ #endif
113
+
114
+ // Point ------------------------------------------------------------------------
115
+
116
+ template <typename T>
117
+ struct Point {
118
+ T x;
119
+ T y;
120
+ #ifdef USINGZ
121
+ z_type z;
122
+
123
+ template <typename T2>
124
+ inline void Init(const T2 x_ = 0, const T2 y_ = 0, const z_type z_ = 0)
125
+ {
126
+ if constexpr (std::is_integral_v<T> &&
127
+ is_round_invocable<T2>::value && !std::is_integral_v<T2>)
128
+ {
129
+ x = static_cast<T>(std::round(x_));
130
+ y = static_cast<T>(std::round(y_));
131
+ z = z_;
132
+ }
133
+ else
134
+ {
135
+ x = static_cast<T>(x_);
136
+ y = static_cast<T>(y_);
137
+ z = z_;
138
+ }
139
+ }
140
+
141
+ explicit Point() : x(0), y(0), z(0) {}
142
+
143
+ template <typename T2>
144
+ Point(const T2 x_, const T2 y_, const z_type z_ = 0)
145
+ {
146
+ Init(x_, y_);
147
+ z = z_;
148
+ }
149
+
150
+ template <typename T2>
151
+ explicit Point(const Point<T2>& p)
152
+ {
153
+ Init(p.x, p.y, p.z);
154
+ }
155
+
156
+ template <typename T2>
157
+ explicit Point(const Point<T2>& p, z_type z_)
158
+ {
159
+ Init(p.x, p.y, z_);
160
+ }
161
+
162
+ Point operator * (const double scale) const
163
+ {
164
+ return Point(x * scale, y * scale, z);
165
+ }
166
+
167
+ void SetZ(const z_type z_value) { z = z_value; }
168
+
169
+ friend std::ostream& operator<<(std::ostream& os, const Point& point)
170
+ {
171
+ os << point.x << "," << point.y << "," << point.z;
172
+ return os;
173
+ }
174
+
175
+ #else
176
+
177
+ template <typename T2>
178
+ inline void Init(const T2 x_ = 0, const T2 y_ = 0)
179
+ {
180
+ if constexpr (std::is_integral_v<T> &&
181
+ is_round_invocable<T2>::value && !std::is_integral_v<T2>)
182
+ {
183
+ x = static_cast<T>(std::round(x_));
184
+ y = static_cast<T>(std::round(y_));
185
+ }
186
+ else
187
+ {
188
+ x = static_cast<T>(x_);
189
+ y = static_cast<T>(y_);
190
+ }
191
+ }
192
+
193
+ explicit Point() : x(0), y(0) {}
194
+
195
+ template <typename T2>
196
+ Point(const T2 x_, const T2 y_) { Init(x_, y_); }
197
+
198
+ template <typename T2>
199
+ explicit Point(const Point<T2>& p) { Init(p.x, p.y); }
200
+
201
+ Point operator * (const double scale) const
202
+ {
203
+ return Point(x * scale, y * scale);
204
+ }
205
+
206
+ friend std::ostream& operator<<(std::ostream& os, const Point& point)
207
+ {
208
+ os << point.x << "," << point.y;
209
+ return os;
210
+ }
211
+ #endif
212
+
213
+ friend bool operator==(const Point& a, const Point& b)
214
+ {
215
+ return a.x == b.x && a.y == b.y;
216
+ }
217
+
218
+ friend bool operator!=(const Point& a, const Point& b)
219
+ {
220
+ return !(a == b);
221
+ }
222
+
223
+ inline Point<T> operator-() const
224
+ {
225
+ return Point<T>(-x, -y);
226
+ }
227
+
228
+ inline Point operator+(const Point& b) const
229
+ {
230
+ return Point(x + b.x, y + b.y);
231
+ }
232
+
233
+ inline Point operator-(const Point& b) const
234
+ {
235
+ return Point(x - b.x, y - b.y);
236
+ }
237
+
238
+ inline void Negate() { x = -x; y = -y; }
239
+
240
+ };
241
+
242
+ //nb: using 'using' here (instead of typedef) as they can be used in templates
243
+ using Point64 = Point<int64_t>;
244
+ using PointD = Point<double>;
245
+
246
+ template <typename T>
247
+ using Path = std::vector<Point<T>>;
248
+ template <typename T>
249
+ using Paths = std::vector<Path<T>>;
250
+
251
+ template <typename T, typename T2=T>
252
+ Path<T>& operator<<(Path<T>& poly, const Point<T2>& p)
253
+ {
254
+ poly.emplace_back(p);
255
+ return poly;
256
+ }
257
+
258
+ template <typename T>
259
+ Paths<T>& operator<<(Paths<T>& polys, const Path<T>& p)
260
+ {
261
+ polys.emplace_back(p);
262
+ return polys;
263
+ }
264
+
265
+ using Path64 = Path<int64_t>;
266
+ using PathD = Path<double>;
267
+ using Paths64 = std::vector< Path64>;
268
+ using PathsD = std::vector< PathD>;
269
+
270
+ static const Point64 InvalidPoint64 = Point64(
271
+ (std::numeric_limits<int64_t>::max)(),
272
+ (std::numeric_limits<int64_t>::max)());
273
+ static const PointD InvalidPointD = PointD(
274
+ (std::numeric_limits<double>::max)(),
275
+ (std::numeric_limits<double>::max)());
276
+
277
+ template<typename T>
278
+ static inline Point<T> MidPoint(const Point<T>& p1, const Point<T>& p2)
279
+ {
280
+ Point<T> result;
281
+ result.x = (p1.x + p2.x) / 2;
282
+ result.y = (p1.y + p2.y) / 2;
283
+ return result;
284
+ }
285
+
286
+ // Rect ------------------------------------------------------------------------
287
+
288
+ template <typename T>
289
+ struct Rect;
290
+
291
+ using Rect64 = Rect<int64_t>;
292
+ using RectD = Rect<double>;
293
+
294
+ template <typename T>
295
+ struct Rect {
296
+ T left;
297
+ T top;
298
+ T right;
299
+ T bottom;
300
+
301
+ Rect(T l, T t, T r, T b) :
302
+ left(l),
303
+ top(t),
304
+ right(r),
305
+ bottom(b) {}
306
+
307
+ Rect(bool is_valid = true)
308
+ {
309
+ if (is_valid)
310
+ {
311
+ left = right = top = bottom = 0;
312
+ }
313
+ else
314
+ {
315
+ left = top = (std::numeric_limits<T>::max)();
316
+ right = bottom = std::numeric_limits<T>::lowest();
317
+ }
318
+ }
319
+
320
+ static Rect<T> InvalidRect()
321
+ {
322
+ return {
323
+ (std::numeric_limits<T>::max)(),
324
+ (std::numeric_limits<T>::max)(),
325
+ std::numeric_limits<T>::lowest(),
326
+ std::numeric_limits<T>::lowest() };
327
+ }
328
+
329
+ bool IsValid() const { return left != (std::numeric_limits<T>::max)(); }
330
+
331
+ T Width() const { return right - left; }
332
+ T Height() const { return bottom - top; }
333
+ void Width(T width) { right = left + width; }
334
+ void Height(T height) { bottom = top + height; }
335
+
336
+ Point<T> MidPoint() const
337
+ {
338
+ return Point<T>((left + right) / 2, (top + bottom) / 2);
339
+ }
340
+
341
+ Path<T> AsPath() const
342
+ {
343
+ Path<T> result;
344
+ result.reserve(4);
345
+ result.emplace_back(left, top);
346
+ result.emplace_back(right, top);
347
+ result.emplace_back(right, bottom);
348
+ result.emplace_back(left, bottom);
349
+ return result;
350
+ }
351
+
352
+ bool Contains(const Point<T>& pt) const
353
+ {
354
+ return pt.x > left && pt.x < right&& pt.y > top && pt.y < bottom;
355
+ }
356
+
357
+ bool Contains(const Rect<T>& rec) const
358
+ {
359
+ return rec.left >= left && rec.right <= right &&
360
+ rec.top >= top && rec.bottom <= bottom;
361
+ }
362
+
363
+ void Scale(double scale) {
364
+ left *= scale;
365
+ top *= scale;
366
+ right *= scale;
367
+ bottom *= scale;
368
+ }
369
+
370
+ bool IsEmpty() const { return bottom <= top || right <= left; }
371
+
372
+ bool Intersects(const Rect<T>& rec) const
373
+ {
374
+ return ((std::max)(left, rec.left) <= (std::min)(right, rec.right)) &&
375
+ ((std::max)(top, rec.top) <= (std::min)(bottom, rec.bottom));
376
+ }
377
+
378
+ bool operator==(const Rect<T>& other) const {
379
+ return left == other.left && right == other.right &&
380
+ top == other.top && bottom == other.bottom;
381
+ }
382
+
383
+ Rect<T>& operator+=(const Rect<T>& other)
384
+ {
385
+ left = (std::min)(left, other.left);
386
+ top = (std::min)(top, other.top);
387
+ right = (std::max)(right, other.right);
388
+ bottom = (std::max)(bottom, other.bottom);
389
+ return *this;
390
+ }
391
+
392
+ Rect<T> operator+(const Rect<T>& other) const
393
+ {
394
+ Rect<T> result = *this;
395
+ result += other;
396
+ return result;
397
+ }
398
+
399
+ friend std::ostream& operator<<(std::ostream& os, const Rect<T>& rect) {
400
+ os << "(" << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom << ") ";
401
+ return os;
402
+ }
403
+ };
404
+
405
+ template <typename T1, typename T2>
406
+ inline Rect<T1> ScaleRect(const Rect<T2>& rect, double scale)
407
+ {
408
+ Rect<T1> result;
409
+
410
+ if constexpr (std::is_integral_v<T1> &&
411
+ is_round_invocable<T2>::value && !std::is_integral_v<T2>)
412
+ {
413
+ result.left = static_cast<T1>(std::round(rect.left * scale));
414
+ result.top = static_cast<T1>(std::round(rect.top * scale));
415
+ result.right = static_cast<T1>(std::round(rect.right * scale));
416
+ result.bottom = static_cast<T1>(std::round(rect.bottom * scale));
417
+ }
418
+ else
419
+ {
420
+ result.left = static_cast<T1>(rect.left * scale);
421
+ result.top = static_cast<T1>(rect.top * scale);
422
+ result.right = static_cast<T1>(rect.right * scale);
423
+ result.bottom = static_cast<T1>(rect.bottom * scale);
424
+ }
425
+ return result;
426
+ }
427
+
428
+ static const Rect64 InvalidRect64 = Rect64::InvalidRect();
429
+ static const RectD InvalidRectD = RectD::InvalidRect();
430
+
431
+ template <typename T>
432
+ Rect<T> GetBounds(const Path<T>& path)
433
+ {
434
+ T xmin = (std::numeric_limits<T>::max)();
435
+ T ymin = (std::numeric_limits<T>::max)();
436
+ T xmax = std::numeric_limits<T>::lowest();
437
+ T ymax = std::numeric_limits<T>::lowest();
438
+ for (const auto& p : path)
439
+ {
440
+ if (p.x < xmin) xmin = p.x;
441
+ if (p.x > xmax) xmax = p.x;
442
+ if (p.y < ymin) ymin = p.y;
443
+ if (p.y > ymax) ymax = p.y;
444
+ }
445
+ return Rect<T>(xmin, ymin, xmax, ymax);
446
+ }
447
+
448
+ template <typename T>
449
+ Rect<T> GetBounds(const Paths<T>& paths)
450
+ {
451
+ T xmin = (std::numeric_limits<T>::max)();
452
+ T ymin = (std::numeric_limits<T>::max)();
453
+ T xmax = std::numeric_limits<T>::lowest();
454
+ T ymax = std::numeric_limits<T>::lowest();
455
+ for (const Path<T>& path : paths)
456
+ for (const Point<T>& p : path)
457
+ {
458
+ if (p.x < xmin) xmin = p.x;
459
+ if (p.x > xmax) xmax = p.x;
460
+ if (p.y < ymin) ymin = p.y;
461
+ if (p.y > ymax) ymax = p.y;
462
+ }
463
+ return Rect<T>(xmin, ymin, xmax, ymax);
464
+ }
465
+
466
+ template <typename T, typename T2>
467
+ Rect<T> GetBounds(const Path<T2>& path)
468
+ {
469
+ T xmin = (std::numeric_limits<T>::max)();
470
+ T ymin = (std::numeric_limits<T>::max)();
471
+ T xmax = std::numeric_limits<T>::lowest();
472
+ T ymax = std::numeric_limits<T>::lowest();
473
+ for (const auto& p : path)
474
+ {
475
+ if (p.x < xmin) xmin = static_cast<T>(p.x);
476
+ if (p.x > xmax) xmax = static_cast<T>(p.x);
477
+ if (p.y < ymin) ymin = static_cast<T>(p.y);
478
+ if (p.y > ymax) ymax = static_cast<T>(p.y);
479
+ }
480
+ return Rect<T>(xmin, ymin, xmax, ymax);
481
+ }
482
+
483
+ template <typename T, typename T2>
484
+ Rect<T> GetBounds(const Paths<T2>& paths)
485
+ {
486
+ T xmin = (std::numeric_limits<T>::max)();
487
+ T ymin = (std::numeric_limits<T>::max)();
488
+ T xmax = std::numeric_limits<T>::lowest();
489
+ T ymax = std::numeric_limits<T>::lowest();
490
+ for (const Path<T2>& path : paths)
491
+ for (const Point<T2>& p : path)
492
+ {
493
+ if (p.x < xmin) xmin = static_cast<T>(p.x);
494
+ if (p.x > xmax) xmax = static_cast<T>(p.x);
495
+ if (p.y < ymin) ymin = static_cast<T>(p.y);
496
+ if (p.y > ymax) ymax = static_cast<T>(p.y);
497
+ }
498
+ return Rect<T>(xmin, ymin, xmax, ymax);
499
+ }
500
+
501
+ template <typename T>
502
+ std::ostream& operator << (std::ostream& outstream, const Path<T>& path)
503
+ {
504
+ if (!path.empty())
505
+ {
506
+ auto pt = path.cbegin(), last = path.cend() - 1;
507
+ while (pt != last)
508
+ outstream << *pt++ << ", ";
509
+ outstream << *last << std::endl;
510
+ }
511
+ return outstream;
512
+ }
513
+
514
+ template <typename T>
515
+ std::ostream& operator << (std::ostream& outstream, const Paths<T>& paths)
516
+ {
517
+ for (auto p : paths)
518
+ outstream << p;
519
+ return outstream;
520
+ }
521
+
522
+
523
+ template <typename T1, typename T2>
524
+ inline Path<T1> ScalePath(const Path<T2>& path,
525
+ double scale_x, double scale_y, int& error_code)
526
+ {
527
+ Path<T1> result;
528
+ if (scale_x == 0 || scale_y == 0)
529
+ {
530
+ error_code |= scale_error_i;
531
+ DoError(scale_error_i);
532
+ // if no exception, treat as non-fatal error
533
+ if (scale_x == 0) scale_x = 1.0;
534
+ if (scale_y == 0) scale_y = 1.0;
535
+ }
536
+
537
+ result.reserve(path.size());
538
+ #ifdef USINGZ
539
+ std::transform(path.begin(), path.end(), back_inserter(result),
540
+ [scale_x, scale_y](const auto& pt)
541
+ { return Point<T1>(pt.x * scale_x, pt.y * scale_y, pt.z); });
542
+ #else
543
+ std::transform(path.begin(), path.end(), back_inserter(result),
544
+ [scale_x, scale_y](const auto& pt)
545
+ { return Point<T1>(pt.x * scale_x, pt.y * scale_y); });
546
+ #endif
547
+ return result;
548
+ }
549
+
550
+ template <typename T1, typename T2>
551
+ inline Path<T1> ScalePath(const Path<T2>& path,
552
+ double scale, int& error_code)
553
+ {
554
+ return ScalePath<T1, T2>(path, scale, scale, error_code);
555
+ }
556
+
557
+ template <typename T1, typename T2>
558
+ inline Paths<T1> ScalePaths(const Paths<T2>& paths,
559
+ double scale_x, double scale_y, int& error_code)
560
+ {
561
+ Paths<T1> result;
562
+
563
+ if constexpr (std::is_integral_v<T1>)
564
+ {
565
+ RectD r = GetBounds<double, T2>(paths);
566
+ if ((r.left * scale_x) < min_coord ||
567
+ (r.right * scale_x) > max_coord ||
568
+ (r.top * scale_y) < min_coord ||
569
+ (r.bottom * scale_y) > max_coord)
570
+ {
571
+ error_code |= range_error_i;
572
+ DoError(range_error_i);
573
+ return result; // empty path
574
+ }
575
+ }
576
+
577
+ result.reserve(paths.size());
578
+ std::transform(paths.begin(), paths.end(), back_inserter(result),
579
+ [=, &error_code](const auto& path)
580
+ { return ScalePath<T1, T2>(path, scale_x, scale_y, error_code); });
581
+ return result;
582
+ }
583
+
584
+ template <typename T1, typename T2>
585
+ inline Paths<T1> ScalePaths(const Paths<T2>& paths,
586
+ double scale, int& error_code)
587
+ {
588
+ return ScalePaths<T1, T2>(paths, scale, scale, error_code);
589
+ }
590
+
591
+ template <typename T1, typename T2>
592
+ inline Path<T1> TransformPath(const Path<T2>& path)
593
+ {
594
+ Path<T1> result;
595
+ result.reserve(path.size());
596
+ std::transform(path.cbegin(), path.cend(), std::back_inserter(result),
597
+ [](const Point<T2>& pt) {return Point<T1>(pt); });
598
+ return result;
599
+ }
600
+
601
+ template <typename T1, typename T2>
602
+ inline Paths<T1> TransformPaths(const Paths<T2>& paths)
603
+ {
604
+ Paths<T1> result;
605
+ std::transform(paths.cbegin(), paths.cend(), std::back_inserter(result),
606
+ [](const Path<T2>& path) {return TransformPath<T1, T2>(path); });
607
+ return result;
608
+ }
609
+
610
+ template<typename T>
611
+ inline double Sqr(T val)
612
+ {
613
+ return static_cast<double>(val) * static_cast<double>(val);
614
+ }
615
+
616
+ template<typename T>
617
+ inline bool NearEqual(const Point<T>& p1,
618
+ const Point<T>& p2, double max_dist_sqrd)
619
+ {
620
+ return Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y) < max_dist_sqrd;
621
+ }
622
+
623
+ template<typename T>
624
+ inline Path<T> StripNearEqual(const Path<T>& path,
625
+ double max_dist_sqrd, bool is_closed_path)
626
+ {
627
+ if (path.size() == 0) return Path<T>();
628
+ Path<T> result;
629
+ result.reserve(path.size());
630
+ typename Path<T>::const_iterator path_iter = path.cbegin();
631
+ Point<T> first_pt = *path_iter++, last_pt = first_pt;
632
+ result.emplace_back(first_pt);
633
+ for (; path_iter != path.cend(); ++path_iter)
634
+ {
635
+ if (!NearEqual(*path_iter, last_pt, max_dist_sqrd))
636
+ {
637
+ last_pt = *path_iter;
638
+ result.emplace_back(last_pt);
639
+ }
640
+ }
641
+ if (!is_closed_path) return result;
642
+ while (result.size() > 1 &&
643
+ NearEqual(result.back(), first_pt, max_dist_sqrd)) result.pop_back();
644
+ return result;
645
+ }
646
+
647
+ template<typename T>
648
+ inline Paths<T> StripNearEqual(const Paths<T>& paths,
649
+ double max_dist_sqrd, bool is_closed_path)
650
+ {
651
+ Paths<T> result;
652
+ result.reserve(paths.size());
653
+ for (typename Paths<T>::const_iterator paths_citer = paths.cbegin();
654
+ paths_citer != paths.cend(); ++paths_citer)
655
+ {
656
+ result.emplace_back(std::move(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path)));
657
+ }
658
+ return result;
659
+ }
660
+
661
+ template<typename T>
662
+ inline void StripDuplicates( Path<T>& path, bool is_closed_path)
663
+ {
664
+ //https://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector#:~:text=Let%27s%20compare%20three%20approaches%3A
665
+ path.erase(std::unique(path.begin(), path.end()), path.end());
666
+ if (is_closed_path)
667
+ while (path.size() > 1 && path.back() == path.front()) path.pop_back();
668
+ }
669
+
670
+ template<typename T>
671
+ inline void StripDuplicates( Paths<T>& paths, bool is_closed_path)
672
+ {
673
+ for (typename Paths<T>::iterator paths_citer = paths.begin();
674
+ paths_citer != paths.end(); ++paths_citer)
675
+ {
676
+ StripDuplicates(*paths_citer, is_closed_path);
677
+ }
678
+ }
679
+
680
+ // Miscellaneous ------------------------------------------------------------
681
+
682
+ inline void CheckPrecisionRange(int& precision, int& error_code)
683
+ {
684
+ if (precision >= -CLIPPER2_MAX_DEC_PRECISION &&
685
+ precision <= CLIPPER2_MAX_DEC_PRECISION) return;
686
+ error_code |= precision_error_i; // non-fatal error
687
+ DoError(precision_error_i); // does nothing when exceptions are disabled
688
+ precision = precision > 0 ? CLIPPER2_MAX_DEC_PRECISION : -CLIPPER2_MAX_DEC_PRECISION;
689
+ }
690
+
691
+ inline void CheckPrecisionRange(int& precision)
692
+ {
693
+ int error_code = 0;
694
+ CheckPrecisionRange(precision, error_code);
695
+ }
696
+
697
+ inline int TriSign(int64_t x) // returns 0, 1 or -1
698
+ {
699
+ return (x > 0) - (x < 0);
700
+ }
701
+
702
+ struct UInt128Struct
703
+ {
704
+ const uint64_t lo = 0;
705
+ const uint64_t hi = 0;
706
+
707
+ bool operator==(const UInt128Struct& other) const
708
+ {
709
+ return lo == other.lo && hi == other.hi;
710
+ }
711
+
712
+ };
713
+
714
+ inline UInt128Struct MultiplyUInt64(uint64_t a, uint64_t b) // #834, #835
715
+ {
716
+ // note to self - lamba expressions follow
717
+ const auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
718
+ const auto hi = [](uint64_t x) { return x >> 32; };
719
+
720
+ const uint64_t x1 = lo(a) * lo(b);
721
+ const uint64_t x2 = hi(a) * lo(b) + hi(x1);
722
+ const uint64_t x3 = lo(a) * hi(b) + lo(x2);
723
+ return { uint64_t(lo(x3) << 32 | lo(x1)), uint64_t(hi(a) * hi(b) + hi(x2) + hi(x3)) };
724
+ }
725
+
726
+ // returns true if (and only if) a * b == c * d
727
+ inline bool ProductsAreEqual(int64_t a, int64_t b, int64_t c, int64_t d)
728
+ {
729
+ #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
730
+ const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
731
+ const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
732
+ return ab == cd;
733
+ #else
734
+ // nb: unsigned values needed for calculating overflow carry
735
+ const auto abs_a = static_cast<uint64_t>(std::abs(a));
736
+ const auto abs_b = static_cast<uint64_t>(std::abs(b));
737
+ const auto abs_c = static_cast<uint64_t>(std::abs(c));
738
+ const auto abs_d = static_cast<uint64_t>(std::abs(d));
739
+
740
+ const auto ab = MultiplyUInt64(abs_a, abs_b);
741
+ const auto cd = MultiplyUInt64(abs_c, abs_d);
742
+
743
+ // nb: it's important to differentiate 0 values here from other values
744
+ const auto sign_ab = TriSign(a) * TriSign(b);
745
+ const auto sign_cd = TriSign(c) * TriSign(d);
746
+
747
+ return ab == cd && sign_ab == sign_cd;
748
+ #endif
749
+ }
750
+
751
+ template <typename T>
752
+ inline int CrossProductSign(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3)
753
+ {
754
+ const auto a = pt2.x - pt1.x;
755
+ const auto b = pt3.y - pt2.y;
756
+ const auto c = pt2.y - pt1.y;
757
+ const auto d = pt3.x - pt2.x;
758
+
759
+ #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
760
+ const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
761
+ const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
762
+ if (ab > cd) return 1;
763
+ else if (ab < cd) return -1;
764
+ else return 0;
765
+ #else
766
+ const auto ab = MultiplyUInt64(std::abs(a), std::abs(b));
767
+ const auto cd = MultiplyUInt64(std::abs(c), std::abs(d));
768
+
769
+ const auto sign_ab = TriSign(a) * TriSign(b);
770
+ const auto sign_cd = TriSign(c) * TriSign(d);
771
+
772
+ if (sign_ab == sign_cd)
773
+ {
774
+ int result;
775
+ if (ab.hi == cd.hi)
776
+ {
777
+ if (ab.lo == cd.lo) return 0;
778
+ result = (ab.lo > cd.lo) ? 1 : -1;
779
+ }
780
+ else result = (ab.hi > cd.hi) ? 1 : -1;
781
+ return (sign_ab > 0) ? result : -result;
782
+ }
783
+ return (sign_ab > sign_cd) ? 1 : -1;
784
+ #endif
785
+ }
786
+
787
+ template <typename T>
788
+ inline bool IsCollinear(const Point<T>& pt1,
789
+ const Point<T>& sharedPt, const Point<T>& pt2) // #777
790
+ {
791
+ const auto a = sharedPt.x - pt1.x;
792
+ const auto b = pt2.y - sharedPt.y;
793
+ const auto c = sharedPt.y - pt1.y;
794
+ const auto d = pt2.x - sharedPt.x;
795
+ // When checking for collinearity with very large coordinate values
796
+ // then ProductsAreEqual is more accurate than using CrossProduct.
797
+ return ProductsAreEqual(a, b, c, d);
798
+ }
799
+
800
+
801
+ template <typename T>
802
+ inline double CrossProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
803
+ return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.y -
804
+ pt2.y) - static_cast<double>(pt2.y - pt1.y) * static_cast<double>(pt3.x - pt2.x));
805
+ }
806
+
807
+ template <typename T>
808
+ inline double CrossProduct(const Point<T>& vec1, const Point<T>& vec2)
809
+ {
810
+ return static_cast<double>(vec1.y * vec2.x) - static_cast<double>(vec2.y * vec1.x);
811
+ }
812
+
813
+ template <typename T>
814
+ inline double DotProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
815
+ return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.x - pt2.x) +
816
+ static_cast<double>(pt2.y - pt1.y) * static_cast<double>(pt3.y - pt2.y));
817
+ }
818
+
819
+ template <typename T>
820
+ inline double DotProduct(const Point<T>& vec1, const Point<T>& vec2)
821
+ {
822
+ return static_cast<double>(vec1.x * vec2.x) + static_cast<double>(vec1.y * vec2.y);
823
+ }
824
+
825
+ template <typename T>
826
+ inline double DistanceSqr(const Point<T> pt1, const Point<T> pt2)
827
+ {
828
+ return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y);
829
+ }
830
+
831
+ template <typename T>
832
+ inline double PerpendicDistFromLineSqrd(const Point<T>& pt,
833
+ const Point<T>& line1, const Point<T>& line2)
834
+ {
835
+ //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
836
+ //see https://en.wikipedia.org/wiki/Perpendicular_distance
837
+ double a = static_cast<double>(pt.x - line1.x);
838
+ double b = static_cast<double>(pt.y - line1.y);
839
+ double c = static_cast<double>(line2.x - line1.x);
840
+ double d = static_cast<double>(line2.y - line1.y);
841
+ if (c == 0 && d == 0) return 0;
842
+ return Sqr(a * d - c * b) / (c * c + d * d);
843
+ }
844
+
845
+ template <typename T>
846
+ inline double Area(const Path<T>& path)
847
+ {
848
+ size_t cnt = path.size();
849
+ if (cnt < 3) return 0.0;
850
+ double a = 0.0;
851
+ typename Path<T>::const_iterator it1, it2 = path.cend() - 1, stop = it2;
852
+ if (!(cnt & 1)) ++stop;
853
+ for (it1 = path.cbegin(); it1 != stop;)
854
+ {
855
+ a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
856
+ it2 = it1 + 1;
857
+ a += static_cast<double>(it1->y + it2->y) * (it1->x - it2->x);
858
+ it1 += 2;
859
+ }
860
+ if (cnt & 1)
861
+ a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
862
+ return (a * 0.5);
863
+ }
864
+
865
+ template <typename T>
866
+ inline double Area(const Paths<T>& paths)
867
+ {
868
+ double a = 0.0;
869
+ for (typename Paths<T>::const_iterator paths_iter = paths.cbegin();
870
+ paths_iter != paths.cend(); ++paths_iter)
871
+ {
872
+ a += Area<T>(*paths_iter);
873
+ }
874
+ return a;
875
+ }
876
+
877
+ template <typename T>
878
+ inline bool IsPositive(const Path<T>& poly)
879
+ {
880
+ // A curve has positive orientation [and area] if a region 'R'
881
+ // is on the left when traveling around the outside of 'R'.
882
+ //https://mathworld.wolfram.com/CurveOrientation.html
883
+ //nb: This statement is premised on using Cartesian coordinates
884
+ return Area<T>(poly) >= 0;
885
+ }
886
+
887
+ // GetLineIntersectPt - a 'true' result is non-parallel. The 'ip' will also
888
+ // be constrained to seg1. However, it's possible that 'ip' won't be inside
889
+ // seg2, even when 'ip' hasn't been constrained (ie 'ip' is inside seg1).
890
+
891
+ #if defined(CLIPPER2_HI_PRECISION) && CLIPPER2_HI_PRECISION
892
+ // caution: this will compromise performance
893
+ // https://github.com/AngusJohnson/Clipper2/issues/317#issuecomment-1314023253
894
+ // See also CPP/BenchMark/GetIntersectPtBenchmark.cpp
895
+ #define CC_MIN(x,y) ((x)>(y)?(y):(x))
896
+ #define CC_MAX(x,y) ((x)<(y)?(y):(x))
897
+ template<typename T>
898
+ inline bool GetLineIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
899
+ const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
900
+ {
901
+ double ln1dy = static_cast<double>(ln1b.y - ln1a.y);
902
+ double ln1dx = static_cast<double>(ln1a.x - ln1b.x);
903
+ double ln2dy = static_cast<double>(ln2b.y - ln2a.y);
904
+ double ln2dx = static_cast<double>(ln2a.x - ln2b.x);
905
+ double det = (ln2dy * ln1dx) - (ln1dy * ln2dx);
906
+ if (det == 0.0) return false;
907
+ T bb0minx = CC_MIN(ln1a.x, ln1b.x);
908
+ T bb0miny = CC_MIN(ln1a.y, ln1b.y);
909
+ T bb0maxx = CC_MAX(ln1a.x, ln1b.x);
910
+ T bb0maxy = CC_MAX(ln1a.y, ln1b.y);
911
+ T bb1minx = CC_MIN(ln2a.x, ln2b.x);
912
+ T bb1miny = CC_MIN(ln2a.y, ln2b.y);
913
+ T bb1maxx = CC_MAX(ln2a.x, ln2b.x);
914
+ T bb1maxy = CC_MAX(ln2a.y, ln2b.y);
915
+
916
+ if constexpr (std::is_integral_v<T>)
917
+ {
918
+ int64_t originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) >> 1;
919
+ int64_t originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) >> 1;
920
+ double ln0c = (ln1dy * static_cast<double>(ln1a.x - originx)) +
921
+ (ln1dx * static_cast<double>(ln1a.y - originy));
922
+ double ln1c = (ln2dy * static_cast<double>(ln2a.x - originx)) +
923
+ (ln2dx * static_cast<double>(ln2a.y - originy));
924
+ double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det;
925
+ double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det;
926
+
927
+ ip.x = originx + (T)nearbyint(hitx);
928
+ ip.y = originy + (T)nearbyint(hity);
929
+ }
930
+ else
931
+ {
932
+ double originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) / 2.0;
933
+ double originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) / 2.0;
934
+ double ln0c = (ln1dy * static_cast<double>(ln1a.x - originx)) +
935
+ (ln1dx * static_cast<double>(ln1a.y - originy));
936
+ double ln1c = (ln2dy * static_cast<double>(ln2a.x - originx)) +
937
+ (ln2dx * static_cast<double>(ln2a.y - originy));
938
+ double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det;
939
+ double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det;
940
+
941
+ ip.x = originx + static_cast<T>(hitx);
942
+ ip.y = originy + static_cast<T>(hity);
943
+ }
944
+ #ifdef USINGZ
945
+ ip.z = 0;
946
+ #endif
947
+ return true;
948
+ }
949
+ #else
950
+ template<typename T>
951
+ inline bool GetLineIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
952
+ const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
953
+ {
954
+ // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
955
+ double dx1 = static_cast<double>(ln1b.x - ln1a.x);
956
+ double dy1 = static_cast<double>(ln1b.y - ln1a.y);
957
+ double dx2 = static_cast<double>(ln2b.x - ln2a.x);
958
+ double dy2 = static_cast<double>(ln2b.y - ln2a.y);
959
+
960
+ double det = dy1 * dx2 - dy2 * dx1;
961
+ if (det == 0.0) return false;
962
+ double t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det;
963
+ if (t <= 0.0) ip = ln1a;
964
+ else if (t >= 1.0) ip = ln1b;
965
+ else
966
+ {
967
+ ip.x = static_cast<T>(ln1a.x + t * dx1);
968
+ ip.y = static_cast<T>(ln1a.y + t * dy1);
969
+ #ifdef USINGZ
970
+ ip.z = 0;
971
+ #endif
972
+ }
973
+ return true;
974
+ }
975
+ #endif
976
+
977
+ template<typename T>
978
+ inline Point<T> TranslatePoint(const Point<T>& pt, double dx, double dy)
979
+ {
980
+ #ifdef USINGZ
981
+ return Point<T>(pt.x + dx, pt.y + dy, pt.z);
982
+ #else
983
+ return Point<T>(pt.x + dx, pt.y + dy);
984
+ #endif
985
+ }
986
+
987
+
988
+ template<typename T>
989
+ inline Point<T> ReflectPoint(const Point<T>& pt, const Point<T>& pivot)
990
+ {
991
+ #ifdef USINGZ
992
+ return Point<T>(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y), pt.z);
993
+ #else
994
+ return Point<T>(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y));
995
+ #endif
996
+ }
997
+
998
+ template<typename T>
999
+ inline int GetSign(const T& val)
1000
+ {
1001
+ if (!val) return 0;
1002
+ return (val > 0) ? 1 : -1;
1003
+ }
1004
+
1005
+ inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
1006
+ const Point64& seg2a, const Point64& seg2b, bool inclusive = false)
1007
+ {
1008
+ double dy1 = static_cast<double>(seg1b.y - seg1a.y);
1009
+ double dx1 = static_cast<double>(seg1b.x - seg1a.x);
1010
+ double dy2 = static_cast<double>(seg2b.y - seg2a.y);
1011
+ double dx2 = static_cast<double>(seg2b.x - seg2a.x);
1012
+ double cp = dy1 * dx2 - dy2 * dx1;
1013
+ if (cp == 0) return false; // ie parallel segments
1014
+
1015
+ if (inclusive)
1016
+ {
1017
+ //result **includes** segments that touch at an end point
1018
+ double t = ((seg1a.x - seg2a.x) * dy2 - (seg1a.y - seg2a.y) * dx2);
1019
+ if (t == 0) return true;
1020
+ if (t > 0)
1021
+ {
1022
+ if (cp < 0 || t > cp) return false;
1023
+ }
1024
+ else if (cp > 0 || t < cp) return false; // false when t more neg. than cp
1025
+
1026
+ t = ((seg1a.x - seg2a.x) * dy1 - (seg1a.y - seg2a.y) * dx1);
1027
+ if (t == 0) return true;
1028
+ if (t > 0) return (cp > 0 && t <= cp);
1029
+ else return (cp < 0 && t >= cp); // true when t less neg. than cp
1030
+ }
1031
+ else
1032
+ {
1033
+ //result **excludes** segments that touch at an end point
1034
+ double t = ((seg1a.x - seg2a.x) * dy2 - (seg1a.y - seg2a.y) * dx2);
1035
+ if (t == 0) return false;
1036
+ if (t > 0)
1037
+ {
1038
+ if (cp < 0 || t >= cp) return false;
1039
+ }
1040
+ else if (cp > 0 || t <= cp ) return false; // false when t more neg. than cp
1041
+
1042
+ t = ((seg1a.x - seg2a.x) * dy1 - (seg1a.y - seg2a.y) * dx1);
1043
+ if (t == 0) return false;
1044
+ if (t > 0) return (cp > 0 && t < cp);
1045
+ else return (cp < 0 && t > cp); // true when t less neg. than cp
1046
+ }
1047
+ }
1048
+
1049
+ template<typename T>
1050
+ inline Point<T> GetClosestPointOnSegment(const Point<T>& offPt,
1051
+ const Point<T>& seg1, const Point<T>& seg2)
1052
+ {
1053
+ if (seg1.x == seg2.x && seg1.y == seg2.y) return seg1;
1054
+ double dx = static_cast<double>(seg2.x - seg1.x);
1055
+ double dy = static_cast<double>(seg2.y - seg1.y);
1056
+ double q =
1057
+ (static_cast<double>(offPt.x - seg1.x) * dx +
1058
+ static_cast<double>(offPt.y - seg1.y) * dy) /
1059
+ (Sqr(dx) + Sqr(dy));
1060
+ if (q < 0) q = 0; else if (q > 1) q = 1;
1061
+ if constexpr (std::is_integral_v<T>)
1062
+ return Point<T>(
1063
+ seg1.x + static_cast<T>(nearbyint(q * dx)),
1064
+ seg1.y + static_cast<T>(nearbyint(q * dy)));
1065
+ else
1066
+ return Point<T>(
1067
+ seg1.x + static_cast<T>(q * dx),
1068
+ seg1.y + static_cast<T>(q * dy));
1069
+ }
1070
+
1071
+ enum class PointInPolygonResult { IsOn, IsInside, IsOutside };
1072
+
1073
+ template <typename T>
1074
+ inline PointInPolygonResult PointInPolygon(const Point<T>& pt, const Path<T>& polygon)
1075
+ {
1076
+ if (polygon.size() < 3)
1077
+ return PointInPolygonResult::IsOutside;
1078
+
1079
+ int val = 0;
1080
+ typename Path<T>::const_iterator cbegin = polygon.cbegin(), first = cbegin, curr, prev;
1081
+ typename Path<T>::const_iterator cend = polygon.cend();
1082
+
1083
+ while (first != cend && first->y == pt.y) ++first;
1084
+ if (first == cend) // not a proper polygon
1085
+ return PointInPolygonResult::IsOutside;
1086
+
1087
+ bool is_above = first->y < pt.y, starting_above = is_above;
1088
+ curr = first +1;
1089
+ while (true)
1090
+ {
1091
+ if (curr == cend)
1092
+ {
1093
+ if (cend == first || first == cbegin) break;
1094
+ cend = first;
1095
+ curr = cbegin;
1096
+ }
1097
+
1098
+ if (is_above)
1099
+ {
1100
+ while (curr != cend && curr->y < pt.y) ++curr;
1101
+ if (curr == cend) continue;
1102
+ }
1103
+ else
1104
+ {
1105
+ while (curr != cend && curr->y > pt.y) ++curr;
1106
+ if (curr == cend) continue;
1107
+ }
1108
+
1109
+ if (curr == cbegin)
1110
+ prev = polygon.cend() - 1; //nb: NOT cend (since might equal first)
1111
+ else
1112
+ prev = curr - 1;
1113
+
1114
+ if (curr->y == pt.y)
1115
+ {
1116
+ if (curr->x == pt.x ||
1117
+ (curr->y == prev->y &&
1118
+ ((pt.x < prev->x) != (pt.x < curr->x))))
1119
+ return PointInPolygonResult::IsOn;
1120
+ ++curr;
1121
+ if (curr == first) break;
1122
+ continue;
1123
+ }
1124
+
1125
+ if (pt.x < curr->x && pt.x < prev->x)
1126
+ {
1127
+ // we're only interested in edges crossing on the left
1128
+ }
1129
+ else if (pt.x > prev->x && pt.x > curr->x)
1130
+ val = 1 - val; // toggle val
1131
+ else
1132
+ {
1133
+ int d = CrossProductSign(*prev, *curr, pt);
1134
+ if (d == 0) return PointInPolygonResult::IsOn;
1135
+ if ((d < 0) == is_above) val = 1 - val;
1136
+ }
1137
+ is_above = !is_above;
1138
+ ++curr;
1139
+ }
1140
+
1141
+ if (is_above != starting_above)
1142
+ {
1143
+ cend = polygon.cend();
1144
+ if (curr == cend) curr = cbegin;
1145
+ if (curr == cbegin) prev = cend - 1;
1146
+ else prev = curr - 1;
1147
+ int d = CrossProductSign(*prev, *curr, pt);
1148
+ if (d == 0) return PointInPolygonResult::IsOn;
1149
+ if ((d < 0) == is_above) val = 1 - val;
1150
+ }
1151
+
1152
+ return (val == 0) ?
1153
+ PointInPolygonResult::IsOutside :
1154
+ PointInPolygonResult::IsInside;
1155
+ }
1156
+
1157
+ } // namespace
1158
+
1159
+ #endif // CLIPPER_CORE_H