polygon_clipper 6.4.2.1

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.
@@ -0,0 +1,406 @@
1
+ /*******************************************************************************
2
+ * *
3
+ * Author : Angus Johnson *
4
+ * Version : 6.4.2 *
5
+ * Date : 27 February 2017 *
6
+ * Website : http://www.angusj.com *
7
+ * Copyright : Angus Johnson 2010-2017 *
8
+ * *
9
+ * License: *
10
+ * Use, modification & distribution is subject to Boost Software License Ver 1. *
11
+ * http://www.boost.org/LICENSE_1_0.txt *
12
+ * *
13
+ * Attributions: *
14
+ * The code in this library is an extension of Bala Vatti's clipping algorithm: *
15
+ * "A generic solution to polygon clipping" *
16
+ * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
17
+ * http://portal.acm.org/citation.cfm?id=129906 *
18
+ * *
19
+ * Computer graphics and geometric modeling: implementation and algorithms *
20
+ * By Max K. Agoston *
21
+ * Springer; 1 edition (January 4, 2005) *
22
+ * http://books.google.com/books?q=vatti+clipping+agoston *
23
+ * *
24
+ * See also: *
25
+ * "Polygon Offsetting by Computing Winding Numbers" *
26
+ * Paper no. DETC2005-85513 pp. 565-575 *
27
+ * ASME 2005 International Design Engineering Technical Conferences *
28
+ * and Computers and Information in Engineering Conference (IDETC/CIE2005) *
29
+ * September 24-28, 2005 , Long Beach, California, USA *
30
+ * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
31
+ * *
32
+ *******************************************************************************/
33
+
34
+ #ifndef clipper_hpp
35
+ #define clipper_hpp
36
+
37
+ #define CLIPPER_VERSION "6.4.2"
38
+
39
+ //use_int32: When enabled 32bit ints are used instead of 64bit ints. This
40
+ //improve performance but coordinate values are limited to the range +/- 46340
41
+ //#define use_int32
42
+
43
+ //use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
44
+ //#define use_xyz
45
+
46
+ //use_lines: Enables line clipping. Adds a very minor cost to performance.
47
+ #define use_lines
48
+
49
+ //use_deprecated: Enables temporary support for the obsolete functions
50
+ //#define use_deprecated
51
+
52
+ #include <vector>
53
+ #include <list>
54
+ #include <set>
55
+ #include <stdexcept>
56
+ #include <cstring>
57
+ #include <cstdlib>
58
+ #include <ostream>
59
+ #include <functional>
60
+ #include <queue>
61
+
62
+ namespace ClipperLib {
63
+
64
+ enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
65
+ enum PolyType { ptSubject, ptClip };
66
+ //By far the most widely used winding rules for polygon filling are
67
+ //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
68
+ //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
69
+ //see http://glprogramming.com/red/chapter11.html
70
+ enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
71
+
72
+ #ifdef use_int32
73
+ typedef int cInt;
74
+ static cInt const loRange = 0x7FFF;
75
+ static cInt const hiRange = 0x7FFF;
76
+ #else
77
+ typedef signed long long cInt;
78
+ static cInt const loRange = 0x3FFFFFFF;
79
+ static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
80
+ typedef signed long long long64; //used by Int128 class
81
+ typedef unsigned long long ulong64;
82
+
83
+ #endif
84
+
85
+ struct IntPoint {
86
+ cInt X;
87
+ cInt Y;
88
+ #ifdef use_xyz
89
+ cInt Z;
90
+ IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};
91
+ #else
92
+ IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};
93
+ #endif
94
+
95
+ friend inline bool operator== (const IntPoint& a, const IntPoint& b)
96
+ {
97
+ return a.X == b.X && a.Y == b.Y;
98
+ }
99
+ friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
100
+ {
101
+ return a.X != b.X || a.Y != b.Y;
102
+ }
103
+ };
104
+ //------------------------------------------------------------------------------
105
+
106
+ typedef std::vector< IntPoint > Path;
107
+ typedef std::vector< Path > Paths;
108
+
109
+ inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
110
+ inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
111
+
112
+ std::ostream& operator <<(std::ostream &s, const IntPoint &p);
113
+ std::ostream& operator <<(std::ostream &s, const Path &p);
114
+ std::ostream& operator <<(std::ostream &s, const Paths &p);
115
+
116
+ struct DoublePoint
117
+ {
118
+ double X;
119
+ double Y;
120
+ DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
121
+ DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
122
+ };
123
+ //------------------------------------------------------------------------------
124
+
125
+ #ifdef use_xyz
126
+ typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
127
+ #endif
128
+
129
+ enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
130
+ enum JoinType {jtSquare, jtRound, jtMiter};
131
+ enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
132
+
133
+ class PolyNode;
134
+ typedef std::vector< PolyNode* > PolyNodes;
135
+
136
+ class PolyNode
137
+ {
138
+ public:
139
+ PolyNode();
140
+ virtual ~PolyNode(){};
141
+ Path Contour;
142
+ PolyNodes Childs;
143
+ PolyNode* Parent;
144
+ PolyNode* GetNext() const;
145
+ bool IsHole() const;
146
+ bool IsOpen() const;
147
+ int ChildCount() const;
148
+ private:
149
+ //PolyNode& operator =(PolyNode& other);
150
+ unsigned Index; //node index in Parent.Childs
151
+ bool m_IsOpen;
152
+ JoinType m_jointype;
153
+ EndType m_endtype;
154
+ PolyNode* GetNextSiblingUp() const;
155
+ void AddChild(PolyNode& child);
156
+ friend class Clipper; //to access Index
157
+ friend class ClipperOffset;
158
+ };
159
+
160
+ class PolyTree: public PolyNode
161
+ {
162
+ public:
163
+ ~PolyTree(){ Clear(); };
164
+ PolyNode* GetFirst() const;
165
+ void Clear();
166
+ int Total() const;
167
+ private:
168
+ //PolyTree& operator =(PolyTree& other);
169
+ PolyNodes AllNodes;
170
+ friend class Clipper; //to access AllNodes
171
+ };
172
+
173
+ bool Orientation(const Path &poly);
174
+ double Area(const Path &poly);
175
+ int PointInPolygon(const IntPoint &pt, const Path &path);
176
+
177
+ void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
178
+ void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
179
+ void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
180
+
181
+ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
182
+ void CleanPolygon(Path& poly, double distance = 1.415);
183
+ void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
184
+ void CleanPolygons(Paths& polys, double distance = 1.415);
185
+
186
+ void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
187
+ void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
188
+ void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
189
+
190
+ void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
191
+ void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
192
+ void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
193
+
194
+ void ReversePath(Path& p);
195
+ void ReversePaths(Paths& p);
196
+
197
+ struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
198
+
199
+ //enums that are used internally ...
200
+ enum EdgeSide { esLeft = 1, esRight = 2};
201
+
202
+ //forward declarations (for stuff used internally) ...
203
+ struct TEdge;
204
+ struct IntersectNode;
205
+ struct LocalMinimum;
206
+ struct OutPt;
207
+ struct OutRec;
208
+ struct Join;
209
+
210
+ typedef std::vector < OutRec* > PolyOutList;
211
+ typedef std::vector < TEdge* > EdgeList;
212
+ typedef std::vector < Join* > JoinList;
213
+ typedef std::vector < IntersectNode* > IntersectList;
214
+
215
+ //------------------------------------------------------------------------------
216
+
217
+ //ClipperBase is the ancestor to the Clipper class. It should not be
218
+ //instantiated directly. This class simply abstracts the conversion of sets of
219
+ //polygon coordinates into edge objects that are stored in a LocalMinima list.
220
+ class ClipperBase
221
+ {
222
+ public:
223
+ ClipperBase();
224
+ virtual ~ClipperBase();
225
+ virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
226
+ bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
227
+ virtual void Clear();
228
+ IntRect GetBounds();
229
+ bool PreserveCollinear() {return m_PreserveCollinear;};
230
+ void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
231
+ protected:
232
+ void DisposeLocalMinimaList();
233
+ TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
234
+ virtual void Reset();
235
+ TEdge* ProcessBound(TEdge* E, bool IsClockwise);
236
+ void InsertScanbeam(const cInt Y);
237
+ bool PopScanbeam(cInt &Y);
238
+ bool LocalMinimaPending();
239
+ bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin);
240
+ OutRec* CreateOutRec();
241
+ void DisposeAllOutRecs();
242
+ void DisposeOutRec(PolyOutList::size_type index);
243
+ void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
244
+ void DeleteFromAEL(TEdge *e);
245
+ void UpdateEdgeIntoAEL(TEdge *&e);
246
+
247
+ typedef std::vector<LocalMinimum> MinimaList;
248
+ MinimaList::iterator m_CurrentLM;
249
+ MinimaList m_MinimaList;
250
+
251
+ bool m_UseFullRange;
252
+ EdgeList m_edges;
253
+ bool m_PreserveCollinear;
254
+ bool m_HasOpenPaths;
255
+ PolyOutList m_PolyOuts;
256
+ TEdge *m_ActiveEdges;
257
+
258
+ typedef std::priority_queue<cInt> ScanbeamList;
259
+ ScanbeamList m_Scanbeam;
260
+ };
261
+ //------------------------------------------------------------------------------
262
+
263
+ class Clipper : public virtual ClipperBase
264
+ {
265
+ public:
266
+ Clipper(int initOptions = 0);
267
+ bool Execute(ClipType clipType,
268
+ Paths &solution,
269
+ PolyFillType fillType = pftEvenOdd);
270
+ bool Execute(ClipType clipType,
271
+ Paths &solution,
272
+ PolyFillType subjFillType,
273
+ PolyFillType clipFillType);
274
+ bool Execute(ClipType clipType,
275
+ PolyTree &polytree,
276
+ PolyFillType fillType = pftEvenOdd);
277
+ bool Execute(ClipType clipType,
278
+ PolyTree &polytree,
279
+ PolyFillType subjFillType,
280
+ PolyFillType clipFillType);
281
+ bool ReverseSolution() { return m_ReverseOutput; };
282
+ void ReverseSolution(bool value) {m_ReverseOutput = value;};
283
+ bool StrictlySimple() {return m_StrictSimple;};
284
+ void StrictlySimple(bool value) {m_StrictSimple = value;};
285
+ //set the callback function for z value filling on intersections (otherwise Z is 0)
286
+ #ifdef use_xyz
287
+ void ZFillFunction(ZFillCallback zFillFunc);
288
+ #endif
289
+ protected:
290
+ virtual bool ExecuteInternal();
291
+ private:
292
+ JoinList m_Joins;
293
+ JoinList m_GhostJoins;
294
+ IntersectList m_IntersectList;
295
+ ClipType m_ClipType;
296
+ typedef std::list<cInt> MaximaList;
297
+ MaximaList m_Maxima;
298
+ TEdge *m_SortedEdges;
299
+ bool m_ExecuteLocked;
300
+ PolyFillType m_ClipFillType;
301
+ PolyFillType m_SubjFillType;
302
+ bool m_ReverseOutput;
303
+ bool m_UsingPolyTree;
304
+ bool m_StrictSimple;
305
+ #ifdef use_xyz
306
+ ZFillCallback m_ZFill; //custom callback
307
+ #endif
308
+ void SetWindingCount(TEdge& edge);
309
+ bool IsEvenOddFillType(const TEdge& edge) const;
310
+ bool IsEvenOddAltFillType(const TEdge& edge) const;
311
+ void InsertLocalMinimaIntoAEL(const cInt botY);
312
+ void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
313
+ void AddEdgeToSEL(TEdge *edge);
314
+ bool PopEdgeFromSEL(TEdge *&edge);
315
+ void CopyAELToSEL();
316
+ void DeleteFromSEL(TEdge *e);
317
+ void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
318
+ bool IsContributing(const TEdge& edge) const;
319
+ bool IsTopHorz(const cInt XPos);
320
+ void DoMaxima(TEdge *e);
321
+ void ProcessHorizontals();
322
+ void ProcessHorizontal(TEdge *horzEdge);
323
+ void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
324
+ OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
325
+ OutRec* GetOutRec(int idx);
326
+ void AppendPolygon(TEdge *e1, TEdge *e2);
327
+ void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
328
+ OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
329
+ OutPt* GetLastOutPt(TEdge *e);
330
+ bool ProcessIntersections(const cInt topY);
331
+ void BuildIntersectList(const cInt topY);
332
+ void ProcessIntersectList();
333
+ void ProcessEdgesAtTopOfScanbeam(const cInt topY);
334
+ void BuildResult(Paths& polys);
335
+ void BuildResult2(PolyTree& polytree);
336
+ void SetHoleState(TEdge *e, OutRec *outrec);
337
+ void DisposeIntersectNodes();
338
+ bool FixupIntersectionOrder();
339
+ void FixupOutPolygon(OutRec &outrec);
340
+ void FixupOutPolyline(OutRec &outrec);
341
+ bool IsHole(TEdge *e);
342
+ bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
343
+ void FixHoleLinkage(OutRec &outrec);
344
+ void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
345
+ void ClearJoins();
346
+ void ClearGhostJoins();
347
+ void AddGhostJoin(OutPt *op, const IntPoint offPt);
348
+ bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
349
+ void JoinCommonEdges();
350
+ void DoSimplePolygons();
351
+ void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
352
+ void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec);
353
+ void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec);
354
+ #ifdef use_xyz
355
+ void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
356
+ #endif
357
+ };
358
+ //------------------------------------------------------------------------------
359
+
360
+ class ClipperOffset
361
+ {
362
+ public:
363
+ ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
364
+ ~ClipperOffset();
365
+ void AddPath(const Path& path, JoinType joinType, EndType endType);
366
+ void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
367
+ void Execute(Paths& solution, double delta);
368
+ void Execute(PolyTree& solution, double delta);
369
+ void Clear();
370
+ double MiterLimit;
371
+ double ArcTolerance;
372
+ private:
373
+ Paths m_destPolys;
374
+ Path m_srcPoly;
375
+ Path m_destPoly;
376
+ std::vector<DoublePoint> m_normals;
377
+ double m_delta, m_sinA, m_sin, m_cos;
378
+ double m_miterLim, m_StepsPerRad;
379
+ IntPoint m_lowest;
380
+ PolyNode m_polyNodes;
381
+
382
+ void FixOrientations();
383
+ void DoOffset(double delta);
384
+ void OffsetPoint(int j, int& k, JoinType jointype);
385
+ void DoSquare(int j, int k);
386
+ void DoMiter(int j, int k, double r);
387
+ void DoRound(int j, int k);
388
+ };
389
+ //------------------------------------------------------------------------------
390
+
391
+ class clipperException : public std::exception
392
+ {
393
+ public:
394
+ clipperException(const char* description): m_descr(description) {}
395
+ virtual ~clipperException() throw() {}
396
+ virtual const char* what() const throw() {return m_descr.c_str();}
397
+ private:
398
+ std::string m_descr;
399
+ };
400
+ //------------------------------------------------------------------------------
401
+
402
+ } //ClipperLib namespace
403
+
404
+ #endif //clipper_hpp
405
+
406
+
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
4
+
5
+ dir_config('clipper')
6
+ create_makefile('clipper')
@@ -0,0 +1,379 @@
1
+ /*
2
+ * Clipper Ruby Bindings
3
+ * Copyright 2010 Mike Owens <http://mike.filespanker.com/>
4
+ *
5
+ * Released under the same terms as Clipper.
6
+ *
7
+ */
8
+
9
+ #include <clipper.hpp>
10
+ #include <ruby.h>
11
+
12
+ #ifndef DBL2NUM
13
+ # define DBL2NUM rb_float_new
14
+ #endif
15
+
16
+ using namespace std;
17
+ using namespace ClipperLib;
18
+
19
+ /**
20
+ * clip types
21
+ * PolyFillTypes
22
+ * http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/ClipType.htm
23
+ */
24
+ static ID id_even_odd;
25
+ static ID id_non_zero;
26
+ static ID id_positive;
27
+ static ID id_negative;
28
+
29
+ /**
30
+ * end types
31
+ * http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/EndType.htm
32
+ */
33
+ static ID id_et_closed_polygon;
34
+ static ID id_et_closed_line;
35
+ static ID id_et_open_square;
36
+ static ID id_et_open_round;
37
+ static ID id_et_open_butt;
38
+
39
+ /**
40
+ * join types
41
+ * http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/JoinType.htm
42
+ */
43
+ static ID id_jt_square;
44
+ static ID id_jt_round;
45
+ static ID id_jt_miter;
46
+
47
+ /**
48
+ * init options
49
+ * http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Methods/Constructor.htm
50
+ */
51
+ // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/ReverseSolution.htm
52
+ static ID id_init_reverse_solution;
53
+ // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm
54
+ static ID id_init_strictly_simple;
55
+ // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/PreserveCollinear.htm
56
+ static ID id_init_preserve_collinear;
57
+
58
+ static inline Clipper*
59
+ XCLIPPER(VALUE x)
60
+ {
61
+ Clipper* clipper;
62
+ Data_Get_Struct(x, Clipper, clipper);
63
+ return clipper;
64
+ }
65
+
66
+ static inline PolyFillType
67
+ sym_to_filltype(VALUE sym)
68
+ {
69
+ ID inp = rb_to_id(sym);
70
+
71
+ if (inp == id_even_odd) {
72
+ return pftEvenOdd;
73
+ } else if (inp == id_non_zero) {
74
+ return pftNonZero;
75
+ } else if (inp == id_positive) {
76
+ return pftPositive;
77
+ } else if (inp == id_negative) {
78
+ return pftNegative;
79
+ }
80
+
81
+ rb_raise(rb_eArgError, "%s", "Expected fill types: even_odd, non_zero, positive, negative");
82
+ }
83
+
84
+ static inline EndType
85
+ sym_to_endtype(VALUE sym)
86
+ {
87
+ ID inp = rb_to_id(sym);
88
+
89
+ if (inp == id_et_closed_polygon) {
90
+ return etClosedPolygon;
91
+ } else if (inp == id_et_closed_line) {
92
+ return etClosedLine;
93
+ } else if (inp == id_et_open_square) {
94
+ return etOpenSquare;
95
+ } else if (inp == id_et_open_round) {
96
+ return etOpenRound;
97
+ } else if (inp == id_et_open_butt) {
98
+ return etOpenButt;
99
+ }
100
+
101
+ rb_raise(rb_eArgError, "%s", "Expected end types: closed_polygon, closed_line, open_square, open_round, open_butt");
102
+ }
103
+
104
+ static inline JoinType
105
+ sym_to_jointype(VALUE sym)
106
+ {
107
+ ID inp = rb_to_id(sym);
108
+
109
+ if (inp == id_jt_square) {
110
+ return jtSquare;
111
+ } else if (inp == id_jt_round) {
112
+ return jtRound;
113
+ } else if (inp == id_jt_miter) {
114
+ return jtMiter;
115
+ }
116
+
117
+ rb_raise(rb_eArgError, "%s", "Expected join types: square, round, milter");
118
+ }
119
+
120
+ static inline InitOptions
121
+ sym_to_initoptions(VALUE sym)
122
+ {
123
+ ID inp = rb_to_id(sym);
124
+
125
+ if (inp == id_init_preserve_collinear) {
126
+ return ioPreserveCollinear;
127
+ } else if (inp == id_init_reverse_solution) {
128
+ return ioReverseSolution;
129
+ } else if (inp == id_init_strictly_simple) {
130
+ return ioStrictlySimple;
131
+ }
132
+
133
+ rb_raise(rb_eArgError, "%s", "Expected init options: preserve_collinear, reverse_solution, strictly_simple");
134
+ }
135
+
136
+ extern "C" {
137
+
138
+ static inline IntPoint
139
+ xy_to_intpoint(VALUE px, VALUE py, double multiplier) {
140
+ return IntPoint((long64)(NUM2DBL(px) * multiplier), (long64)(NUM2DBL(py) * multiplier));
141
+ }
142
+
143
+ static void
144
+ ary_to_path(VALUE ary, Path* poly, double multiplier)
145
+ {
146
+ const char* earg =
147
+ "Paths have format: [[p0_x, p0_y], [p1_x, p1_y], ...]";
148
+
149
+ Check_Type(ary, T_ARRAY);
150
+
151
+ for(long i = 0; i != RARRAY_LEN(ary); i++) {
152
+ VALUE sub = rb_ary_entry(ary, i);
153
+ Check_Type(sub, T_ARRAY);
154
+
155
+ if(RARRAY_LEN(sub) != 2) {
156
+ rb_raise(rb_eArgError, "%s", earg);
157
+ }
158
+
159
+ VALUE px = rb_ary_entry(sub, 0);
160
+ VALUE py = rb_ary_entry(sub, 1);
161
+ poly->push_back(xy_to_intpoint(px, py, multiplier));
162
+ }
163
+ }
164
+
165
+ static void
166
+ ary_to_paths(VALUE ary, Paths* paths, double multiplier)
167
+ {
168
+ Check_Type(ary, T_ARRAY);
169
+ for(long i = 0; i != RARRAY_LEN(ary); i++) {
170
+ Path p;
171
+ VALUE sub = rb_ary_entry(ary, i);
172
+ Check_Type(sub, T_ARRAY);
173
+ ary_to_path(sub, &p, multiplier);
174
+ paths->push_back(p);
175
+ }
176
+ }
177
+
178
+ static void
179
+ rbclipper_free(void* ptr)
180
+ {
181
+ delete (Clipper*) ptr;
182
+ }
183
+
184
+ static VALUE
185
+ rbclipper_new(VALUE klass)
186
+ {
187
+ Clipper* ptr = new Clipper;
188
+ VALUE r = Data_Wrap_Struct(klass, 0, rbclipper_free, ptr);
189
+ rb_obj_call_init(r, 0, 0);
190
+ return r;
191
+ }
192
+
193
+ static VALUE
194
+ rbclipper_initialize(int argc, VALUE* argv, VALUE self)
195
+ {
196
+ VALUE multiplier;
197
+
198
+ if (argc == 1) {
199
+ multiplier = argv[0];
200
+ } else if (argc == 0) {
201
+ multiplier = INT2NUM(1048576); // 2 ^ 10
202
+ } else {
203
+ rb_raise(rb_eArgError, "wrong number of arguments");
204
+ }
205
+
206
+ rb_iv_set(self, "@multiplier", multiplier);
207
+ return self;
208
+ }
209
+
210
+ static VALUE
211
+ rbclipper_add_polygon_internal(VALUE self, VALUE polygon,
212
+ PolyType polytype)
213
+ {
214
+ double multiplier = NUM2DBL(rb_iv_get(self, "@multiplier"));
215
+ Path tmp;
216
+ ary_to_path(polygon, &tmp, multiplier);
217
+ XCLIPPER(self)->AddPath(tmp, polytype, true);
218
+ return Qnil;
219
+ }
220
+
221
+ static VALUE
222
+ rbclipper_add_subject_polygon(VALUE self, VALUE polygon)
223
+ {
224
+ return rbclipper_add_polygon_internal(self, polygon, ptSubject);
225
+ }
226
+
227
+ static VALUE
228
+ rbclipper_add_clip_polygon(VALUE self, VALUE polygon)
229
+ {
230
+ return rbclipper_add_polygon_internal(self, polygon, ptClip);
231
+ }
232
+
233
+ static VALUE
234
+ rbclipper_add_polygons_internal(VALUE self, VALUE polygons,
235
+ PolyType polytype)
236
+ {
237
+ double multiplier = NUM2DBL(rb_iv_get(self, "@multiplier"));
238
+ Paths tmp;
239
+ ary_to_paths(polygons, &tmp, multiplier);
240
+ XCLIPPER(self)->AddPaths(tmp, polytype, true);
241
+ return Qnil;
242
+ }
243
+
244
+ static VALUE
245
+ rbclipper_add_subject_polygons(VALUE self, VALUE polygons)
246
+ {
247
+ return rbclipper_add_polygons_internal(self, polygons, ptSubject);
248
+ }
249
+
250
+ static VALUE
251
+ rbclipper_add_clip_polygons(VALUE self, VALUE polygons)
252
+ {
253
+ return rbclipper_add_polygons_internal(self, polygons, ptClip);
254
+ }
255
+
256
+ static VALUE
257
+ rbclipper_clear(VALUE self)
258
+ {
259
+ XCLIPPER(self)->Clear();
260
+ return Qnil;
261
+ }
262
+
263
+ static VALUE
264
+ rbclipper_execute_internal(VALUE self, ClipType cliptype,
265
+ VALUE subjfill, VALUE clipfill)
266
+ {
267
+ if (NIL_P(subjfill))
268
+ subjfill = ID2SYM(id_even_odd);
269
+
270
+ if (NIL_P(clipfill))
271
+ clipfill = ID2SYM(id_even_odd);
272
+
273
+ double inv_multiplier = 1.0 / NUM2LONG(rb_iv_get(self, "@multiplier"));
274
+
275
+ Paths solution;
276
+ XCLIPPER(self)->Execute((ClipType) cliptype,
277
+ solution,
278
+ sym_to_filltype(subjfill),
279
+ sym_to_filltype(clipfill));
280
+ VALUE r = rb_ary_new();
281
+ for(Paths::iterator i = solution.begin(); i != solution.end(); ++i) {
282
+ VALUE sub = rb_ary_new();
283
+ for(Path::iterator p = i->begin(); p != i->end(); ++p) {
284
+ rb_ary_push(sub, rb_ary_new3(2, DBL2NUM(p->X * inv_multiplier), DBL2NUM(p->Y * inv_multiplier)));
285
+ }
286
+ rb_ary_push(r, sub);
287
+ }
288
+
289
+ return r;
290
+ }
291
+
292
+ static VALUE
293
+ rbclipper_intersection(int argc, VALUE* argv, VALUE self)
294
+ {
295
+ VALUE subjfill, clipfill;
296
+ rb_scan_args(argc, argv, "02", &subjfill, &clipfill);
297
+ return rbclipper_execute_internal(self, ctIntersection, subjfill, clipfill);
298
+ }
299
+
300
+ static VALUE
301
+ rbclipper_union(int argc, VALUE* argv, VALUE self)
302
+ {
303
+ VALUE subjfill, clipfill;
304
+
305
+ rb_scan_args(argc, argv, "02", &subjfill, &clipfill);
306
+
307
+ return rbclipper_execute_internal(self, ctUnion, subjfill, clipfill);
308
+ }
309
+
310
+ static VALUE
311
+ rbclipper_difference(int argc, VALUE* argv, VALUE self)
312
+ {
313
+ VALUE subjfill, clipfill;
314
+ rb_scan_args(argc, argv, "02", &subjfill, &clipfill);
315
+ return rbclipper_execute_internal(self, ctDifference, subjfill, clipfill);
316
+ }
317
+
318
+ static VALUE
319
+ rbclipper_xor(int argc, VALUE* argv, VALUE self)
320
+ {
321
+ VALUE subjfill, clipfill;
322
+ rb_scan_args(argc, argv, "02", &subjfill, &clipfill);
323
+ return rbclipper_execute_internal(self, ctXor, subjfill, clipfill);
324
+ }
325
+
326
+ typedef VALUE (*ruby_method)(...);
327
+
328
+ void Init_clipper() {
329
+ // fill types
330
+ id_even_odd = rb_intern("even_odd");
331
+ id_non_zero = rb_intern("non_zero");
332
+ id_positive = rb_intern("positive");
333
+ id_negative = rb_intern("negative");
334
+ // end types
335
+ id_et_closed_polygon = rb_intern("closed_polygon");
336
+ id_et_closed_line = rb_intern("closed_line");
337
+ id_et_open_square = rb_intern("open_square");
338
+ id_et_open_round = rb_intern("open_round");
339
+ id_et_open_butt = rb_intern("open_butt");
340
+ // join types
341
+ id_jt_square = rb_intern("square");
342
+ id_jt_round = rb_intern("round");
343
+ id_jt_miter = rb_intern("miter");
344
+ // init options
345
+ id_init_reverse_solution = rb_intern("reverse_solution");
346
+ id_init_strictly_simple = rb_intern("strictly_simple");
347
+ id_init_preserve_collinear = rb_intern("preserve_collinear");
348
+
349
+ VALUE mod = rb_define_module("Clipper");
350
+ VALUE k = rb_define_class_under(mod, "Clipper", rb_cObject);
351
+
352
+ rb_define_singleton_method(k, "new", (ruby_method) rbclipper_new, 0);
353
+ rb_define_method(k, "initialize",(ruby_method) rbclipper_initialize, -1);
354
+ rb_define_attr(k, "multiplier", 1, 0);
355
+
356
+ rb_define_method(k, "add_subject_polygon",
357
+ (ruby_method) rbclipper_add_subject_polygon, 1);
358
+ rb_define_method(k, "add_clip_polygon",
359
+ (ruby_method) rbclipper_add_clip_polygon, 1);
360
+
361
+ rb_define_method(k, "add_subject_polygons",
362
+ (ruby_method) rbclipper_add_subject_polygons, 1);
363
+ rb_define_method(k, "add_clip_polygons",
364
+ (ruby_method) rbclipper_add_clip_polygons, 1);
365
+
366
+ rb_define_method(k, "clear!",
367
+ (ruby_method) rbclipper_clear, 0);
368
+
369
+ rb_define_method(k, "intersection",
370
+ (ruby_method) rbclipper_intersection, -1);
371
+ rb_define_method(k, "union",
372
+ (ruby_method) rbclipper_union, -1);
373
+ rb_define_method(k, "difference",
374
+ (ruby_method) rbclipper_difference, -1);
375
+ rb_define_method(k, "xor",
376
+ (ruby_method) rbclipper_xor, -1);
377
+ }
378
+
379
+ } /* extern "C" */