openscad-cpp-evaluator 0.29.2__cp312-abi3-win_arm64.whl

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