polygon_clipper 6.4.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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" */