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,635 @@
1
+ /*******************************************************************************
2
+ * Author : Angus Johnson *
3
+ * Date : 17 September 2024 *
4
+ * Website : https://www.angusj.com *
5
+ * Copyright : Angus Johnson 2010-2024 *
6
+ * Purpose : This is the main polygon clipping module *
7
+ * License : https://www.boost.org/LICENSE_1_0.txt *
8
+ *******************************************************************************/
9
+
10
+ #ifndef CLIPPER_ENGINE_H
11
+ #define CLIPPER_ENGINE_H
12
+
13
+ #include "clipper2/clipper.core.h"
14
+ #include <queue>
15
+ #include <functional>
16
+ #include <memory>
17
+
18
+ namespace Clipper2Lib {
19
+
20
+ struct Scanline;
21
+ struct IntersectNode;
22
+ struct Active;
23
+ struct Vertex;
24
+ struct LocalMinima;
25
+ struct OutRec;
26
+ struct HorzSegment;
27
+
28
+ //Note: all clipping operations except for Difference are commutative.
29
+ enum class ClipType { NoClip, Intersection, Union, Difference, Xor };
30
+
31
+ enum class PathType { Subject, Clip };
32
+ enum class JoinWith { NoJoin, Left, Right };
33
+
34
+ enum class VertexFlags : uint32_t {
35
+ Empty = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
36
+ };
37
+
38
+ constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
39
+ {
40
+ return (enum VertexFlags)(uint32_t(a) & uint32_t(b));
41
+ }
42
+
43
+ constexpr enum VertexFlags operator |(enum VertexFlags a, enum VertexFlags b)
44
+ {
45
+ return (enum VertexFlags)(uint32_t(a) | uint32_t(b));
46
+ }
47
+
48
+ struct Vertex {
49
+ Point64 pt;
50
+ Vertex* next = nullptr;
51
+ Vertex* prev = nullptr;
52
+ VertexFlags flags = VertexFlags::Empty;
53
+ };
54
+
55
+ struct OutPt {
56
+ Point64 pt;
57
+ OutPt* next = nullptr;
58
+ OutPt* prev = nullptr;
59
+ OutRec* outrec;
60
+ HorzSegment* horz = nullptr;
61
+
62
+ OutPt(const Point64& pt_, OutRec* outrec_): pt(pt_), outrec(outrec_) {
63
+ next = this;
64
+ prev = this;
65
+ }
66
+ };
67
+
68
+ class PolyPath;
69
+ class PolyPath64;
70
+ class PolyPathD;
71
+ using PolyTree64 = PolyPath64;
72
+ using PolyTreeD = PolyPathD;
73
+
74
+ struct OutRec;
75
+ typedef std::vector<OutRec*> OutRecList;
76
+
77
+ //OutRec: contains a path in the clipping solution. Edges in the AEL will
78
+ //have OutRec pointers assigned when they form part of the clipping solution.
79
+ struct OutRec {
80
+ size_t idx = 0;
81
+ OutRec* owner = nullptr;
82
+ Active* front_edge = nullptr;
83
+ Active* back_edge = nullptr;
84
+ OutPt* pts = nullptr;
85
+ PolyPath* polypath = nullptr;
86
+ OutRecList* splits = nullptr;
87
+ OutRec* recursive_split = nullptr;
88
+ Rect64 bounds = {};
89
+ Path64 path;
90
+ bool is_open = false;
91
+
92
+ ~OutRec() {
93
+ if (splits) delete splits;
94
+ // nb: don't delete the split pointers
95
+ // as these are owned by ClipperBase's outrec_list_
96
+ }
97
+ };
98
+
99
+ ///////////////////////////////////////////////////////////////////
100
+ //Important: UP and DOWN here are premised on Y-axis positive down
101
+ //displays, which is the orientation used in Clipper's development.
102
+ ///////////////////////////////////////////////////////////////////
103
+
104
+ struct Active {
105
+ Point64 bot;
106
+ Point64 top;
107
+ int64_t curr_x = 0; //current (updated at every new scanline)
108
+ double dx = 0.0;
109
+ int wind_dx = 1; //1 or -1 depending on winding direction
110
+ int wind_cnt = 0;
111
+ int wind_cnt2 = 0; //winding count of the opposite polytype
112
+ OutRec* outrec = nullptr;
113
+ //AEL: 'active edge list' (Vatti's AET - active edge table)
114
+ // a linked list of all edges (from left to right) that are present
115
+ // (or 'active') within the current scanbeam (a horizontal 'beam' that
116
+ // sweeps from bottom to top over the paths in the clipping operation).
117
+ Active* prev_in_ael = nullptr;
118
+ Active* next_in_ael = nullptr;
119
+ //SEL: 'sorted edge list' (Vatti's ST - sorted table)
120
+ // linked list used when sorting edges into their new positions at the
121
+ // top of scanbeams, but also (re)used to process horizontals.
122
+ Active* prev_in_sel = nullptr;
123
+ Active* next_in_sel = nullptr;
124
+ Active* jump = nullptr;
125
+ Vertex* vertex_top = nullptr;
126
+ LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti)
127
+ bool is_left_bound = false;
128
+ JoinWith join_with = JoinWith::NoJoin;
129
+ };
130
+
131
+ struct LocalMinima {
132
+ Vertex* vertex;
133
+ PathType polytype;
134
+ bool is_open;
135
+ LocalMinima(Vertex* v, PathType pt, bool open) :
136
+ vertex(v), polytype(pt), is_open(open){}
137
+ };
138
+
139
+ struct IntersectNode {
140
+ Point64 pt;
141
+ Active* edge1;
142
+ Active* edge2;
143
+ IntersectNode() : pt(Point64(0,0)), edge1(NULL), edge2(NULL) {}
144
+ IntersectNode(Active* e1, Active* e2, Point64& pt_) :
145
+ pt(pt_), edge1(e1), edge2(e2) {}
146
+ };
147
+
148
+ struct HorzSegment {
149
+ OutPt* left_op;
150
+ OutPt* right_op = nullptr;
151
+ bool left_to_right = true;
152
+ HorzSegment() : left_op(nullptr) { }
153
+ explicit HorzSegment(OutPt* op) : left_op(op) { }
154
+ };
155
+
156
+ struct HorzJoin {
157
+ OutPt* op1 = nullptr;
158
+ OutPt* op2 = nullptr;
159
+ HorzJoin() {}
160
+ explicit HorzJoin(OutPt* ltr, OutPt* rtl) : op1(ltr), op2(rtl) { }
161
+ };
162
+
163
+ #ifdef USINGZ
164
+ typedef std::function<void(const Point64& e1bot, const Point64& e1top,
165
+ const Point64& e2bot, const Point64& e2top, Point64& pt)> ZCallback64;
166
+
167
+ typedef std::function<void(const PointD& e1bot, const PointD& e1top,
168
+ const PointD& e2bot, const PointD& e2top, PointD& pt)> ZCallbackD;
169
+ #endif
170
+
171
+ typedef std::vector<HorzSegment> HorzSegmentList;
172
+ typedef std::unique_ptr<LocalMinima> LocalMinima_ptr;
173
+ typedef std::vector<LocalMinima_ptr> LocalMinimaList;
174
+ typedef std::vector<IntersectNode> IntersectNodeList;
175
+
176
+ // ReuseableDataContainer64 ------------------------------------------------
177
+
178
+ class ReuseableDataContainer64 {
179
+ private:
180
+ friend class ClipperBase;
181
+ LocalMinimaList minima_list_;
182
+ std::vector<Vertex*> vertex_lists_;
183
+ void AddLocMin(Vertex& vert, PathType polytype, bool is_open);
184
+ public:
185
+ virtual ~ReuseableDataContainer64();
186
+ void Clear();
187
+ void AddPaths(const Paths64& paths, PathType polytype, bool is_open);
188
+ };
189
+
190
+ // ClipperBase -------------------------------------------------------------
191
+
192
+ class ClipperBase {
193
+ private:
194
+ ClipType cliptype_ = ClipType::NoClip;
195
+ FillRule fillrule_ = FillRule::EvenOdd;
196
+ FillRule fillpos = FillRule::Positive;
197
+ int64_t bot_y_ = 0;
198
+ bool minima_list_sorted_ = false;
199
+ bool using_polytree_ = false;
200
+ Active* actives_ = nullptr;
201
+ Active *sel_ = nullptr;
202
+ LocalMinimaList minima_list_; //pointers in case of memory reallocs
203
+ LocalMinimaList::iterator current_locmin_iter_;
204
+ std::vector<Vertex*> vertex_lists_;
205
+ std::priority_queue<int64_t> scanline_list_;
206
+ IntersectNodeList intersect_nodes_;
207
+ HorzSegmentList horz_seg_list_;
208
+ std::vector<HorzJoin> horz_join_list_;
209
+ void Reset();
210
+ inline void InsertScanline(int64_t y);
211
+ inline bool PopScanline(int64_t &y);
212
+ inline bool PopLocalMinima(int64_t y, LocalMinima*& local_minima);
213
+ void DisposeAllOutRecs();
214
+ void DisposeVerticesAndLocalMinima();
215
+ void DeleteEdges(Active*& e);
216
+ inline void AddLocMin(Vertex &vert, PathType polytype, bool is_open);
217
+ bool IsContributingClosed(const Active &e) const;
218
+ inline bool IsContributingOpen(const Active &e) const;
219
+ void SetWindCountForClosedPathEdge(Active &edge);
220
+ void SetWindCountForOpenPathEdge(Active &e);
221
+ void InsertLocalMinimaIntoAEL(int64_t bot_y);
222
+ void InsertLeftEdge(Active &e);
223
+ inline void PushHorz(Active &e);
224
+ inline bool PopHorz(Active *&e);
225
+ inline OutPt* StartOpenPath(Active &e, const Point64& pt);
226
+ inline void UpdateEdgeIntoAEL(Active *e);
227
+ void IntersectEdges(Active &e1, Active &e2, const Point64& pt);
228
+ inline void DeleteFromAEL(Active &e);
229
+ inline void AdjustCurrXAndCopyToSEL(const int64_t top_y);
230
+ void DoIntersections(const int64_t top_y);
231
+ void AddNewIntersectNode(Active &e1, Active &e2, const int64_t top_y);
232
+ bool BuildIntersectList(const int64_t top_y);
233
+ void ProcessIntersectList();
234
+ void SwapPositionsInAEL(Active& edge1, Active& edge2);
235
+ OutRec* NewOutRec();
236
+ OutPt* AddOutPt(const Active &e, const Point64& pt);
237
+ OutPt* AddLocalMinPoly(Active &e1, Active &e2,
238
+ const Point64& pt, bool is_new = false);
239
+ OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt);
240
+ void DoHorizontal(Active &horz);
241
+ bool ResetHorzDirection(const Active &horz, const Vertex* max_vertex,
242
+ int64_t &horz_left, int64_t &horz_right);
243
+ void DoTopOfScanbeam(const int64_t top_y);
244
+ Active *DoMaxima(Active &e);
245
+ void JoinOutrecPaths(Active &e1, Active &e2);
246
+ void FixSelfIntersects(OutRec* outrec);
247
+ void DoSplitOp(OutRec* outRec, OutPt* splitOp);
248
+
249
+ inline void AddTrialHorzJoin(OutPt* op);
250
+ void ConvertHorzSegsToJoins();
251
+ void ProcessHorzJoins();
252
+
253
+ void Split(Active& e, const Point64& pt);
254
+ inline void CheckJoinLeft(Active& e,
255
+ const Point64& pt, bool check_curr_x = false);
256
+ inline void CheckJoinRight(Active& e,
257
+ const Point64& pt, bool check_curr_x = false);
258
+ protected:
259
+ bool preserve_collinear_ = true;
260
+ bool reverse_solution_ = false;
261
+ int error_code_ = 0;
262
+ bool has_open_paths_ = false;
263
+ bool succeeded_ = true;
264
+ OutRecList outrec_list_; //pointers in case list memory reallocated
265
+ bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees);
266
+ void CleanCollinear(OutRec* outrec);
267
+ bool CheckBounds(OutRec* outrec);
268
+ bool CheckSplitOwner(OutRec* outrec, OutRecList* splits);
269
+ void RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath);
270
+ #ifdef USINGZ
271
+ ZCallback64 zCallback_ = nullptr;
272
+ void SetZ(const Active& e1, const Active& e2, Point64& pt);
273
+ #endif
274
+ void CleanUp(); // unlike Clear, CleanUp preserves added paths
275
+ void AddPath(const Path64& path, PathType polytype, bool is_open);
276
+ void AddPaths(const Paths64& paths, PathType polytype, bool is_open);
277
+ public:
278
+ virtual ~ClipperBase();
279
+ int ErrorCode() const { return error_code_; }
280
+ void PreserveCollinear(bool val) { preserve_collinear_ = val; }
281
+ bool PreserveCollinear() const { return preserve_collinear_;}
282
+ void ReverseSolution(bool val) { reverse_solution_ = val; }
283
+ bool ReverseSolution() const { return reverse_solution_; }
284
+ void Clear();
285
+ void AddReuseableData(const ReuseableDataContainer64& reuseable_data);
286
+ #ifdef USINGZ
287
+ int64_t DefaultZ = 0;
288
+ #endif
289
+ };
290
+
291
+ // PolyPath / PolyTree --------------------------------------------------------
292
+
293
+ //PolyTree: is intended as a READ-ONLY data structure for CLOSED paths returned
294
+ //by clipping operations. While this structure is more complex than the
295
+ //alternative Paths structure, it does preserve path 'ownership' - ie those
296
+ //paths that contain (or own) other paths. This will be useful to some users.
297
+
298
+ class PolyPath {
299
+ protected:
300
+ PolyPath* parent_;
301
+ public:
302
+ PolyPath(PolyPath* parent = nullptr): parent_(parent){}
303
+ virtual ~PolyPath() {}
304
+ //https://en.cppreference.com/w/cpp/language/rule_of_three
305
+ PolyPath(const PolyPath&) = delete;
306
+ PolyPath& operator=(const PolyPath&) = delete;
307
+
308
+ unsigned Level() const
309
+ {
310
+ unsigned result = 0;
311
+ const PolyPath* p = parent_;
312
+ while (p) { ++result; p = p->parent_; }
313
+ return result;
314
+ }
315
+
316
+ virtual PolyPath* AddChild(const Path64& path) = 0;
317
+
318
+ virtual void Clear() = 0;
319
+ virtual size_t Count() const { return 0; }
320
+
321
+ const PolyPath* Parent() const { return parent_; }
322
+
323
+ bool IsHole() const
324
+ {
325
+ unsigned lvl = Level();
326
+ //Even levels except level 0
327
+ return lvl && !(lvl & 1);
328
+ }
329
+ };
330
+
331
+ typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
332
+ typedef typename std::vector<std::unique_ptr<PolyPathD>> PolyPathDList;
333
+
334
+ class PolyPath64 : public PolyPath {
335
+ private:
336
+ PolyPath64List childs_;
337
+ Path64 polygon_;
338
+ public:
339
+ explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
340
+ explicit PolyPath64(PolyPath64* parent, const Path64& path) : PolyPath(parent) { polygon_ = path; }
341
+
342
+ ~PolyPath64() override {
343
+ childs_.resize(0);
344
+ }
345
+
346
+ PolyPath64* operator [] (size_t index) const
347
+ {
348
+ return childs_[index].get(); //std::unique_ptr
349
+ }
350
+
351
+ PolyPath64* Child(size_t index) const
352
+ {
353
+ return childs_[index].get();
354
+ }
355
+
356
+ PolyPath64List::const_iterator begin() const { return childs_.cbegin(); }
357
+ PolyPath64List::const_iterator end() const { return childs_.cend(); }
358
+
359
+ PolyPath64* AddChild(const Path64& path) override
360
+ {
361
+ return childs_.emplace_back(std::make_unique<PolyPath64>(this, path)).get();
362
+ }
363
+
364
+ void Clear() override
365
+ {
366
+ childs_.resize(0);
367
+ }
368
+
369
+ size_t Count() const override
370
+ {
371
+ return childs_.size();
372
+ }
373
+
374
+ const Path64& Polygon() const { return polygon_; }
375
+
376
+ double Area() const
377
+ {
378
+ return std::accumulate(childs_.cbegin(), childs_.cend(),
379
+ Clipper2Lib::Area<int64_t>(polygon_),
380
+ [](double a, const auto& child) {return a + child->Area(); });
381
+ }
382
+
383
+ };
384
+
385
+ class PolyPathD : public PolyPath {
386
+ private:
387
+ PolyPathDList childs_;
388
+ double scale_;
389
+ PathD polygon_;
390
+ public:
391
+ explicit PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent)
392
+ {
393
+ scale_ = parent ? parent->scale_ : 1.0;
394
+ }
395
+
396
+ explicit PolyPathD(PolyPathD* parent, const Path64& path) : PolyPath(parent)
397
+ {
398
+ scale_ = parent ? parent->scale_ : 1.0;
399
+ int error_code = 0;
400
+ polygon_ = ScalePath<double, int64_t>(path, scale_, error_code);
401
+ }
402
+
403
+ explicit PolyPathD(PolyPathD* parent, const PathD& path) : PolyPath(parent)
404
+ {
405
+ scale_ = parent ? parent->scale_ : 1.0;
406
+ polygon_ = path;
407
+ }
408
+
409
+ ~PolyPathD() override {
410
+ childs_.resize(0);
411
+ }
412
+
413
+ PolyPathD* operator [] (size_t index) const
414
+ {
415
+ return childs_[index].get();
416
+ }
417
+
418
+ PolyPathD* Child(size_t index) const
419
+ {
420
+ return childs_[index].get();
421
+ }
422
+
423
+ PolyPathDList::const_iterator begin() const { return childs_.cbegin(); }
424
+ PolyPathDList::const_iterator end() const { return childs_.cend(); }
425
+
426
+ void SetScale(double value) { scale_ = value; }
427
+ double Scale() const { return scale_; }
428
+
429
+ PolyPathD* AddChild(const Path64& path) override
430
+ {
431
+ return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
432
+ }
433
+
434
+ PolyPathD* AddChild(const PathD& path)
435
+ {
436
+ return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
437
+ }
438
+
439
+ void Clear() override
440
+ {
441
+ childs_.resize(0);
442
+ }
443
+
444
+ size_t Count() const override
445
+ {
446
+ return childs_.size();
447
+ }
448
+
449
+ const PathD& Polygon() const { return polygon_; }
450
+
451
+ double Area() const
452
+ {
453
+ return std::accumulate(childs_.begin(), childs_.end(),
454
+ Clipper2Lib::Area<double>(polygon_),
455
+ [](double a, const auto& child) {return a + child->Area(); });
456
+ }
457
+ };
458
+
459
+ class Clipper64 : public ClipperBase
460
+ {
461
+ private:
462
+ void BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen);
463
+ void BuildTree64(PolyPath64& polytree, Paths64& open_paths);
464
+ public:
465
+ #ifdef USINGZ
466
+ void SetZCallback(ZCallback64 cb) { zCallback_ = cb; }
467
+ #endif
468
+
469
+ void AddSubject(const Paths64& subjects)
470
+ {
471
+ AddPaths(subjects, PathType::Subject, false);
472
+ }
473
+ void AddOpenSubject(const Paths64& open_subjects)
474
+ {
475
+ AddPaths(open_subjects, PathType::Subject, true);
476
+ }
477
+ void AddClip(const Paths64& clips)
478
+ {
479
+ AddPaths(clips, PathType::Clip, false);
480
+ }
481
+
482
+ bool Execute(ClipType clip_type,
483
+ FillRule fill_rule, Paths64& closed_paths)
484
+ {
485
+ Paths64 dummy;
486
+ return Execute(clip_type, fill_rule, closed_paths, dummy);
487
+ }
488
+
489
+ bool Execute(ClipType clip_type, FillRule fill_rule,
490
+ Paths64& closed_paths, Paths64& open_paths)
491
+ {
492
+ closed_paths.clear();
493
+ open_paths.clear();
494
+ if (ExecuteInternal(clip_type, fill_rule, false))
495
+ BuildPaths64(closed_paths, &open_paths);
496
+ CleanUp();
497
+ return succeeded_;
498
+ }
499
+
500
+ bool Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree)
501
+ {
502
+ Paths64 dummy;
503
+ return Execute(clip_type, fill_rule, polytree, dummy);
504
+ }
505
+
506
+ bool Execute(ClipType clip_type,
507
+ FillRule fill_rule, PolyTree64& polytree, Paths64& open_paths)
508
+ {
509
+ if (ExecuteInternal(clip_type, fill_rule, true))
510
+ {
511
+ open_paths.clear();
512
+ polytree.Clear();
513
+ BuildTree64(polytree, open_paths);
514
+ }
515
+ CleanUp();
516
+ return succeeded_;
517
+ }
518
+ };
519
+
520
+ class ClipperD : public ClipperBase {
521
+ private:
522
+ double scale_ = 1.0, invScale_ = 1.0;
523
+ #ifdef USINGZ
524
+ ZCallbackD zCallbackD_ = nullptr;
525
+ #endif
526
+ void BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen);
527
+ void BuildTreeD(PolyPathD& polytree, PathsD& open_paths);
528
+ public:
529
+ explicit ClipperD(int precision = 2) : ClipperBase()
530
+ {
531
+ CheckPrecisionRange(precision, error_code_);
532
+ // to optimize scaling / descaling precision
533
+ // set the scale to a power of double's radix (2) (#25)
534
+ scale_ = std::pow(std::numeric_limits<double>::radix,
535
+ std::ilogb(std::pow(10, precision)) + 1);
536
+ invScale_ = 1 / scale_;
537
+ }
538
+
539
+ #ifdef USINGZ
540
+ void SetZCallback(ZCallbackD cb) { zCallbackD_ = cb; }
541
+
542
+ void ZCB(const Point64& e1bot, const Point64& e1top,
543
+ const Point64& e2bot, const Point64& e2top, Point64& pt)
544
+ {
545
+ // de-scale (x & y)
546
+ // temporarily convert integers to their initial float values
547
+ // this will slow clipping marginally but will make it much easier
548
+ // to understand the coordinates passed to the callback function
549
+ PointD tmp = PointD(pt) * invScale_;
550
+ PointD e1b = PointD(e1bot) * invScale_;
551
+ PointD e1t = PointD(e1top) * invScale_;
552
+ PointD e2b = PointD(e2bot) * invScale_;
553
+ PointD e2t = PointD(e2top) * invScale_;
554
+ zCallbackD_(e1b,e1t, e2b, e2t, tmp);
555
+ pt.z = tmp.z; // only update 'z'
556
+ }
557
+
558
+ void CheckCallback()
559
+ {
560
+ if(zCallbackD_)
561
+ // if the user defined float point callback has been assigned
562
+ // then assign the proxy callback function
563
+ ClipperBase::zCallback_ =
564
+ std::bind(&ClipperD::ZCB, this, std::placeholders::_1,
565
+ std::placeholders::_2, std::placeholders::_3,
566
+ std::placeholders::_4, std::placeholders::_5);
567
+ else
568
+ ClipperBase::zCallback_ = nullptr;
569
+ }
570
+
571
+ #endif
572
+
573
+ void AddSubject(const PathsD& subjects)
574
+ {
575
+ AddPaths(ScalePaths<int64_t, double>(subjects, scale_, error_code_), PathType::Subject, false);
576
+ }
577
+
578
+ void AddOpenSubject(const PathsD& open_subjects)
579
+ {
580
+ AddPaths(ScalePaths<int64_t, double>(open_subjects, scale_, error_code_), PathType::Subject, true);
581
+ }
582
+
583
+ void AddClip(const PathsD& clips)
584
+ {
585
+ AddPaths(ScalePaths<int64_t, double>(clips, scale_, error_code_), PathType::Clip, false);
586
+ }
587
+
588
+ bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths)
589
+ {
590
+ PathsD dummy;
591
+ return Execute(clip_type, fill_rule, closed_paths, dummy);
592
+ }
593
+
594
+ bool Execute(ClipType clip_type,
595
+ FillRule fill_rule, PathsD& closed_paths, PathsD& open_paths)
596
+ {
597
+ #ifdef USINGZ
598
+ CheckCallback();
599
+ #endif
600
+ if (ExecuteInternal(clip_type, fill_rule, false))
601
+ {
602
+ BuildPathsD(closed_paths, &open_paths);
603
+ }
604
+ CleanUp();
605
+ return succeeded_;
606
+ }
607
+
608
+ bool Execute(ClipType clip_type, FillRule fill_rule, PolyTreeD& polytree)
609
+ {
610
+ PathsD dummy;
611
+ return Execute(clip_type, fill_rule, polytree, dummy);
612
+ }
613
+
614
+ bool Execute(ClipType clip_type,
615
+ FillRule fill_rule, PolyTreeD& polytree, PathsD& open_paths)
616
+ {
617
+ #ifdef USINGZ
618
+ CheckCallback();
619
+ #endif
620
+ if (ExecuteInternal(clip_type, fill_rule, true))
621
+ {
622
+ polytree.Clear();
623
+ polytree.SetScale(invScale_);
624
+ open_paths.clear();
625
+ BuildTreeD(polytree, open_paths);
626
+ }
627
+ CleanUp();
628
+ return succeeded_;
629
+ }
630
+
631
+ };
632
+
633
+ } // namespace
634
+
635
+ #endif // CLIPPER_ENGINE_H