ruby_clipper 5.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,306 @@
1
+ /*******************************************************************************
2
+ * *
3
+ * Author : Angus Johnson *
4
+ * Version : 5.0.1 *
5
+ * Date : 30 December 2012 *
6
+ * Website : http://www.angusj.com *
7
+ * Copyright : Angus Johnson 2010-2012 *
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
+ #include <vector>
38
+ #include <stdexcept>
39
+ #include <cstring>
40
+ #include <cstdlib>
41
+ #include <ostream>
42
+
43
+ namespace ClipperLib {
44
+
45
+ enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
46
+ enum PolyType { ptSubject, ptClip };
47
+ //By far the most widely used winding rules for polygon filling are
48
+ //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
49
+ //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
50
+ //see http://glprogramming.com/red/chapter11.html
51
+ enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
52
+
53
+ typedef signed long long long64;
54
+ typedef unsigned long long ulong64;
55
+
56
+ struct IntPoint {
57
+ public:
58
+ long64 X;
59
+ long64 Y;
60
+ IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {};
61
+ friend std::ostream& operator <<(std::ostream &s, IntPoint &p);
62
+ };
63
+
64
+ typedef std::vector< IntPoint > Polygon;
65
+ typedef std::vector< Polygon > Polygons;
66
+
67
+ std::ostream& operator <<(std::ostream &s, Polygon &p);
68
+ std::ostream& operator <<(std::ostream &s, Polygons &p);
69
+
70
+ struct ExPolygon {
71
+ Polygon outer;
72
+ Polygons holes;
73
+ };
74
+ typedef std::vector< ExPolygon > ExPolygons;
75
+
76
+ enum JoinType { jtSquare, jtRound, jtMiter };
77
+
78
+ bool Orientation(const Polygon &poly);
79
+ double Area(const Polygon &poly);
80
+ void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys,
81
+ double delta, JoinType jointype = jtSquare, double MiterLimit = 2, bool AutoFix = true);
82
+ void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
83
+ void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
84
+ void SimplifyPolygons(Polygons &polys, PolyFillType fillType = pftEvenOdd);
85
+
86
+ void ReversePolygon(Polygon& p);
87
+ void ReversePolygons(Polygons& p);
88
+
89
+ //used internally ...
90
+ enum EdgeSide { esLeft = 1, esRight = 2};
91
+ enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 };
92
+
93
+ struct TEdge {
94
+ long64 xbot;
95
+ long64 ybot;
96
+ long64 xcurr;
97
+ long64 ycurr;
98
+ long64 xtop;
99
+ long64 ytop;
100
+ double dx;
101
+ long64 deltaX;
102
+ long64 deltaY;
103
+ long64 tmpX;
104
+ PolyType polyType;
105
+ EdgeSide side;
106
+ int windDelta; //1 or -1 depending on winding direction
107
+ int windCnt;
108
+ int windCnt2; //winding count of the opposite polytype
109
+ int outIdx;
110
+ TEdge *next;
111
+ TEdge *prev;
112
+ TEdge *nextInLML;
113
+ TEdge *nextInAEL;
114
+ TEdge *prevInAEL;
115
+ TEdge *nextInSEL;
116
+ TEdge *prevInSEL;
117
+ };
118
+
119
+ struct IntersectNode {
120
+ TEdge *edge1;
121
+ TEdge *edge2;
122
+ IntPoint pt;
123
+ IntersectNode *next;
124
+ };
125
+
126
+ struct LocalMinima {
127
+ long64 Y;
128
+ TEdge *leftBound;
129
+ TEdge *rightBound;
130
+ LocalMinima *next;
131
+ };
132
+
133
+ struct Scanbeam {
134
+ long64 Y;
135
+ Scanbeam *next;
136
+ };
137
+
138
+ struct OutPt; //forward declaration
139
+
140
+ struct OutRec {
141
+ int idx;
142
+ bool isHole;
143
+ OutRec *FirstLeft;
144
+ OutRec *AppendLink;
145
+ OutPt *pts;
146
+ OutPt *bottomPt;
147
+ };
148
+
149
+ struct OutPt {
150
+ int idx;
151
+ IntPoint pt;
152
+ OutPt *next;
153
+ OutPt *prev;
154
+ };
155
+
156
+ struct JoinRec {
157
+ IntPoint pt1a;
158
+ IntPoint pt1b;
159
+ int poly1Idx;
160
+ IntPoint pt2a;
161
+ IntPoint pt2b;
162
+ int poly2Idx;
163
+ };
164
+
165
+ struct HorzJoinRec {
166
+ TEdge *edge;
167
+ int savedIdx;
168
+ };
169
+
170
+ struct IntRect { long64 left; long64 top; long64 right; long64 bottom; };
171
+
172
+ typedef std::vector < OutRec* > PolyOutList;
173
+ typedef std::vector < TEdge* > EdgeList;
174
+ typedef std::vector < JoinRec* > JoinList;
175
+ typedef std::vector < HorzJoinRec* > HorzJoinList;
176
+
177
+ //ClipperBase is the ancestor to the Clipper class. It should not be
178
+ //instantiated directly. This class simply abstracts the conversion of sets of
179
+ //polygon coordinates into edge objects that are stored in a LocalMinima list.
180
+ class ClipperBase
181
+ {
182
+ public:
183
+ ClipperBase();
184
+ virtual ~ClipperBase();
185
+ bool AddPolygon(const Polygon &pg, PolyType polyType);
186
+ bool AddPolygons( const Polygons &ppg, PolyType polyType);
187
+ virtual void Clear();
188
+ IntRect GetBounds();
189
+ protected:
190
+ void DisposeLocalMinimaList();
191
+ TEdge* AddBoundsToLML(TEdge *e);
192
+ void PopLocalMinima();
193
+ virtual void Reset();
194
+ void InsertLocalMinima(LocalMinima *newLm);
195
+ LocalMinima *m_CurrentLM;
196
+ LocalMinima *m_MinimaList;
197
+ bool m_UseFullRange;
198
+ EdgeList m_edges;
199
+ };
200
+
201
+ class Clipper : public virtual ClipperBase
202
+ {
203
+ public:
204
+ Clipper();
205
+ ~Clipper();
206
+ bool Execute(ClipType clipType,
207
+ Polygons &solution,
208
+ PolyFillType subjFillType = pftEvenOdd,
209
+ PolyFillType clipFillType = pftEvenOdd);
210
+ bool Execute(ClipType clipType,
211
+ ExPolygons &solution,
212
+ PolyFillType subjFillType = pftEvenOdd,
213
+ PolyFillType clipFillType = pftEvenOdd);
214
+ void Clear();
215
+ bool ReverseSolution() {return m_ReverseOutput;};
216
+ void ReverseSolution(bool value) {m_ReverseOutput = value;};
217
+ protected:
218
+ void Reset();
219
+ virtual bool ExecuteInternal();
220
+ private:
221
+ PolyOutList m_PolyOuts;
222
+ JoinList m_Joins;
223
+ HorzJoinList m_HorizJoins;
224
+ ClipType m_ClipType;
225
+ Scanbeam *m_Scanbeam;
226
+ TEdge *m_ActiveEdges;
227
+ TEdge *m_SortedEdges;
228
+ IntersectNode *m_IntersectNodes;
229
+ bool m_ExecuteLocked;
230
+ PolyFillType m_ClipFillType;
231
+ PolyFillType m_SubjFillType;
232
+ bool m_ReverseOutput;
233
+ bool m_UsingExPolygons;
234
+ void DisposeScanbeamList();
235
+ void SetWindingCount(TEdge& edge);
236
+ bool IsEvenOddFillType(const TEdge& edge) const;
237
+ bool IsEvenOddAltFillType(const TEdge& edge) const;
238
+ void InsertScanbeam(const long64 Y);
239
+ long64 PopScanbeam();
240
+ void InsertLocalMinimaIntoAEL(const long64 botY);
241
+ void InsertEdgeIntoAEL(TEdge *edge);
242
+ void AddEdgeToSEL(TEdge *edge);
243
+ void CopyAELToSEL();
244
+ void DeleteFromSEL(TEdge *e);
245
+ void DeleteFromAEL(TEdge *e);
246
+ void UpdateEdgeIntoAEL(TEdge *&e);
247
+ void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
248
+ bool IsContributing(const TEdge& edge) const;
249
+ bool IsTopHorz(const long64 XPos);
250
+ void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
251
+ void DoMaxima(TEdge *e, long64 topY);
252
+ void ProcessHorizontals();
253
+ void ProcessHorizontal(TEdge *horzEdge);
254
+ void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
255
+ void AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
256
+ void AppendPolygon(TEdge *e1, TEdge *e2);
257
+ void DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
258
+ void DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
259
+ void DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
260
+ void IntersectEdges(TEdge *e1, TEdge *e2,
261
+ const IntPoint &pt, const IntersectProtects protects);
262
+ OutRec* CreateOutRec();
263
+ void AddOutPt(TEdge *e, const IntPoint &pt);
264
+ void DisposeAllPolyPts();
265
+ void DisposeOutRec(PolyOutList::size_type index);
266
+ bool ProcessIntersections(const long64 botY, const long64 topY);
267
+ void AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt);
268
+ void BuildIntersectList(const long64 botY, const long64 topY);
269
+ void ProcessIntersectList();
270
+ void ProcessEdgesAtTopOfScanbeam(const long64 topY);
271
+ void BuildResult(Polygons& polys);
272
+ void BuildResultEx(ExPolygons& polys);
273
+ void SetHoleState(TEdge *e, OutRec *OutRec);
274
+ void DisposeIntersectNodes();
275
+ bool FixupIntersections();
276
+ void FixupOutPolygon(OutRec &outRec);
277
+ bool IsHole(TEdge *e);
278
+ void FixHoleLinkage(OutRec *outRec);
279
+ void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1);
280
+ void ClearJoins();
281
+ void AddHorzJoin(TEdge *e, int idx);
282
+ void ClearHorzJoins();
283
+ bool JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2);
284
+ void FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx);
285
+ void JoinCommonEdges();
286
+ };
287
+
288
+ //------------------------------------------------------------------------------
289
+ //------------------------------------------------------------------------------
290
+
291
+ class clipperException : public std::exception
292
+ {
293
+ public:
294
+ clipperException(const char* description): m_descr(description) {}
295
+ virtual ~clipperException() throw() {}
296
+ virtual const char* what() const throw() {return m_descr.c_str();}
297
+ private:
298
+ std::string m_descr;
299
+ };
300
+ //------------------------------------------------------------------------------
301
+
302
+ } //ClipperLib namespace
303
+
304
+ #endif //clipper_hpp
305
+
306
+
@@ -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,389 @@
1
+ /*
2
+ * Clipper Ruby Bindings
3
+ * Copyright 2010 Mike Owens <http://mike.filespanker.com/>
4
+ * Changed by Dag Rende for Clipper after 2.9
5
+ *
6
+ * Released under the same terms as Clipper.
7
+ *
8
+ */
9
+
10
+ #include <clipper.hpp>
11
+ #include <ruby.h>
12
+
13
+ #ifndef DBL2NUM
14
+ # define DBL2NUM rb_float_new
15
+ #endif
16
+
17
+ using namespace std;
18
+ using namespace ClipperLib;
19
+
20
+ static ID id_even_odd;
21
+ static ID id_non_zero;
22
+ static ID id_positive;
23
+ static ID id_negative;
24
+ static ID id_polygons;
25
+ static ID id_ex_polygons;
26
+ static ID id_jtSquare;
27
+ static ID id_jtMiter;
28
+ static ID id_jtRound;
29
+
30
+ static inline Clipper*
31
+ XCLIPPER(VALUE x)
32
+ {
33
+ Clipper* clipper;
34
+ Data_Get_Struct(x, Clipper, clipper);
35
+ return clipper;
36
+ }
37
+
38
+ static inline PolyFillType
39
+ sym_to_filltype(VALUE sym)
40
+ {
41
+ ID inp = rb_to_id(sym);
42
+
43
+ if (inp == id_even_odd) {
44
+ return pftEvenOdd;
45
+ } else if (inp == id_non_zero) {
46
+ return pftNonZero;
47
+ } else if (inp == id_positive) {
48
+ return pftPositive;
49
+ } else if (inp == id_negative) {
50
+ return pftNegative;
51
+ }
52
+
53
+ rb_raise(rb_eArgError, "%s", "Expected :even_odd, :non_zero, :positive or :negative");
54
+ }
55
+
56
+ static inline JoinType
57
+ sym_to_jointype(VALUE sym)
58
+ {
59
+ ID inp = rb_to_id(sym);
60
+
61
+ if (inp == id_jtSquare) {
62
+ return jtSquare;
63
+ } else if (inp == id_jtMiter) {
64
+ return jtMiter;
65
+ } else if (inp == id_jtRound) {
66
+ return jtRound;
67
+ }
68
+
69
+ rb_raise(rb_eArgError, "%s", "Expected :jtSquare, :jtMiter or :jtRound");
70
+ }
71
+
72
+ extern "C" {
73
+
74
+ static void
75
+ ary_to_polygon(VALUE ary, ClipperLib::Polygon* poly, double multiplier)
76
+ {
77
+ const char* earg =
78
+ "Polygons have format: [[p0_x, p0_y], [p1_x, p1_y], ...]";
79
+
80
+ Check_Type(ary, T_ARRAY);
81
+
82
+ for(long i = 0; i != RARRAY_LEN(ary); i++) {
83
+ VALUE sub = rb_ary_entry(ary, i);
84
+ Check_Type(sub, T_ARRAY);
85
+
86
+ if(RARRAY_LEN(sub) != 2) {
87
+ rb_raise(rb_eArgError, "%s", earg);
88
+ }
89
+
90
+ VALUE px = rb_ary_entry(sub, 0);
91
+ VALUE py = rb_ary_entry(sub, 1);
92
+ poly->push_back(IntPoint((long64)(NUM2DBL(px) * multiplier), (long64)(NUM2DBL(py) * multiplier)));
93
+ }
94
+ }
95
+
96
+ static void
97
+ rbclipper_free(void* ptr)
98
+ {
99
+ delete (Clipper*) ptr;
100
+ }
101
+
102
+ static VALUE
103
+ rbclipper_new(VALUE klass)
104
+ {
105
+ Clipper* ptr = new Clipper;
106
+ VALUE r = Data_Wrap_Struct(klass, 0, rbclipper_free, ptr);
107
+ rb_obj_call_init(r, 0, 0);
108
+ rb_iv_set(r, "@multiplier", INT2NUM(1048576));
109
+ return r;
110
+ }
111
+
112
+ static VALUE
113
+ rbclipper_add_polygon_internal(VALUE self, VALUE polygon,
114
+ PolyType polytype)
115
+ {
116
+ ClipperLib::Polygon tmp;
117
+ double multiplier = NUM2DBL(rb_iv_get(self, "@multiplier"));
118
+ ary_to_polygon(polygon, &tmp, multiplier);
119
+ XCLIPPER(self)->AddPolygon(tmp, polytype);
120
+ return Qnil;
121
+ }
122
+
123
+ static VALUE
124
+ rbclipper_add_polygons_internal(VALUE self, VALUE polygonsValue, PolyType polytype) {
125
+ double multiplier = NUM2DBL(rb_iv_get(self, "@multiplier"));
126
+ Polygons polygons;
127
+ for(long i = 0; i != RARRAY_LEN(polygonsValue); i++) {
128
+ VALUE sub = rb_ary_entry(polygonsValue, i);
129
+ Check_Type(sub, T_ARRAY);
130
+
131
+ ClipperLib::Polygon tmp;
132
+ ary_to_polygon(sub, &tmp, multiplier);
133
+ polygons.push_back(tmp);
134
+ }
135
+ XCLIPPER(self)->AddPolygons(polygons, polytype);
136
+ return Qnil;
137
+ }
138
+
139
+ static VALUE
140
+ rbclipper_add_subject_polygon(VALUE self, VALUE polygon)
141
+ {
142
+ return rbclipper_add_polygon_internal(self, polygon, ptSubject);
143
+ }
144
+
145
+ static VALUE
146
+ rbclipper_add_clip_polygon(VALUE self, VALUE polygon)
147
+ {
148
+ return rbclipper_add_polygon_internal(self, polygon, ptClip);
149
+ }
150
+
151
+ static VALUE
152
+ rbclipper_add_subject_polygons(VALUE self, VALUE polygons)
153
+ {
154
+ return rbclipper_add_polygons_internal(self, polygons, ptSubject);
155
+ }
156
+
157
+ static VALUE
158
+ rbclipper_add_clip_polygons(VALUE self, VALUE polygons)
159
+ {
160
+ return rbclipper_add_polygons_internal(self, polygons, ptClip);
161
+ }
162
+
163
+ static VALUE
164
+ rbclipper_clear(VALUE self)
165
+ {
166
+ XCLIPPER(self)->Clear();
167
+ return Qnil;
168
+ }
169
+
170
+ static VALUE
171
+ rbclipper_multiplier(VALUE self)
172
+ {
173
+ return rb_iv_get(self, "@multiplier");
174
+ }
175
+
176
+ static VALUE
177
+ rbclipper_multiplier_eq(VALUE self, VALUE multiplier)
178
+ {
179
+ rb_iv_set(self, "@multiplier", multiplier);
180
+ return multiplier;
181
+ }
182
+
183
+ static VALUE
184
+ rbclipper_orientation(VALUE self, VALUE polygonValue)
185
+ {
186
+ double multiplier = NUM2DBL(rb_iv_get(self, "@multiplier"));
187
+ ClipperLib::Polygon polygon;
188
+ ary_to_polygon(polygonValue, &polygon, multiplier);
189
+
190
+ Polygons resultPolygons;
191
+ return ClipperLib::Orientation(polygon) ? Qtrue : Qfalse;
192
+ }
193
+
194
+ static VALUE
195
+ rbclipper_area(VALUE self, VALUE polygonValue)
196
+ {
197
+ double multiplier = NUM2DBL(rb_iv_get(self, "@multiplier"));
198
+ ClipperLib::Polygon polygon;
199
+ ary_to_polygon(polygonValue, &polygon, multiplier);
200
+
201
+ Polygons resultPolygons;
202
+ return DBL2NUM(ClipperLib::Area(polygon) / multiplier / multiplier);
203
+ }
204
+
205
+
206
+ static VALUE
207
+ rbclipper_offset_polygons(int argc, VALUE* argv, VALUE self)
208
+ {
209
+ double multiplier = NUM2DBL(rb_iv_get(self, "@multiplier"));
210
+ double inv_multiplier = 1.0 / multiplier;
211
+ VALUE polygonsValue, deltaValue, joinTypeValue, miterLimitValue;
212
+
213
+ rb_scan_args(argc, argv, "31", &polygonsValue, &deltaValue, &joinTypeValue, &miterLimitValue);
214
+
215
+ Polygons polygons;
216
+ for(long i = 0; i != RARRAY_LEN(polygonsValue); i++) {
217
+ VALUE sub = rb_ary_entry(polygonsValue, i);
218
+ Check_Type(sub, T_ARRAY);
219
+
220
+ ClipperLib::Polygon tmp;
221
+ ary_to_polygon(sub, &tmp, multiplier);
222
+ polygons.push_back(tmp);
223
+ }
224
+ double miterLimit = 0;
225
+ if (!NIL_P(miterLimitValue)) {
226
+ miterLimit = NUM2DBL(miterLimitValue);
227
+ }
228
+
229
+ Polygons resultPolygons;
230
+ ClipperLib::OffsetPolygons(polygons, resultPolygons, NUM2DBL(deltaValue) * multiplier, sym_to_jointype(joinTypeValue), miterLimit * multiplier);
231
+
232
+ VALUE r = rb_ary_new();
233
+ for(Polygons::iterator i = resultPolygons.begin(); i != resultPolygons.end(); ++i) {
234
+ VALUE sub = rb_ary_new();
235
+ for(Polygon::iterator p = i->begin(); p != i->end(); ++p) {
236
+ rb_ary_push(sub, rb_ary_new3(2, DBL2NUM(p->X * inv_multiplier), DBL2NUM(p->Y * inv_multiplier)));
237
+ }
238
+ rb_ary_push(r, sub);
239
+ }
240
+ return r;
241
+ }
242
+
243
+ static VALUE
244
+ rbclipper_execute_internal(VALUE self, ClipType cliptype,
245
+ VALUE subjfill, VALUE clipfill, VALUE resulttype)
246
+ {
247
+ if (NIL_P(subjfill)) {
248
+ subjfill = ID2SYM(id_even_odd);
249
+ }
250
+
251
+ if (NIL_P(clipfill)) {
252
+ clipfill = ID2SYM(id_even_odd);
253
+ }
254
+
255
+ if (NIL_P(resulttype)) {
256
+ resulttype = ID2SYM(id_polygons);
257
+ }
258
+
259
+ double inv_multiplier = 1.0 / NUM2LONG(rb_iv_get(self, "@multiplier"));
260
+
261
+ VALUE r = rb_ary_new();
262
+ if (resulttype == ID2SYM(id_polygons)) {
263
+ Polygons solution;
264
+ XCLIPPER(self)->Execute((ClipType) cliptype,
265
+ solution,
266
+ sym_to_filltype(subjfill),
267
+ sym_to_filltype(clipfill));
268
+ for(Polygons::iterator i = solution.begin(); i != solution.end(); ++i) {
269
+ VALUE sub = rb_ary_new();
270
+ for(Polygon::iterator p = i->begin(); p != i->end(); ++p) {
271
+ rb_ary_push(sub, rb_ary_new3(2, DBL2NUM(p->X * inv_multiplier), DBL2NUM(p->Y * inv_multiplier)));
272
+ }
273
+ rb_ary_push(r, sub);
274
+ }
275
+ } else {
276
+ ExPolygons solution;
277
+ XCLIPPER(self)->Execute((ClipType) cliptype,
278
+ solution,
279
+ sym_to_filltype(subjfill),
280
+ sym_to_filltype(clipfill));
281
+ for(ExPolygons::iterator i = solution.begin(); i != solution.end(); ++i) {
282
+ VALUE expolygon_arr = rb_ary_new();
283
+
284
+ VALUE outer_arr = rb_ary_new();
285
+ for(Polygon::iterator p = i->outer.begin(); p != i->outer.end(); ++p) {
286
+ rb_ary_push(outer_arr, rb_ary_new3(2, DBL2NUM(p->X * inv_multiplier), DBL2NUM(p->Y * inv_multiplier)));
287
+ }
288
+ rb_ary_push(expolygon_arr, outer_arr);
289
+
290
+ for(Polygons::iterator ps = i->holes.begin(); ps != i->holes.end(); ++ps) {
291
+ VALUE hole_arr = rb_ary_new();
292
+ for(Polygon::iterator p = ps->begin(); p != ps->end(); ++p) {
293
+ rb_ary_push(hole_arr, rb_ary_new3(2, DBL2NUM(p->X * inv_multiplier), DBL2NUM(p->Y * inv_multiplier)));
294
+ }
295
+ rb_ary_push(expolygon_arr, hole_arr);
296
+ }
297
+
298
+ rb_ary_push(r, expolygon_arr);
299
+ }
300
+ }
301
+ return r;
302
+ }
303
+
304
+ static VALUE
305
+ rbclipper_intersection(int argc, VALUE* argv, VALUE self)
306
+ {
307
+ VALUE subjfill, clipfill, resulttype;
308
+ rb_scan_args(argc, argv, "03", &subjfill, &clipfill, &resulttype);
309
+ return rbclipper_execute_internal(self, ctIntersection, subjfill, clipfill, resulttype);
310
+ }
311
+
312
+ static VALUE
313
+ rbclipper_union(int argc, VALUE* argv, VALUE self)
314
+ {
315
+ VALUE subjfill, clipfill, resulttype;
316
+ rb_scan_args(argc, argv, "03", &subjfill, &clipfill, &resulttype);
317
+ return rbclipper_execute_internal(self, ctUnion, subjfill, clipfill, resulttype);
318
+ }
319
+
320
+
321
+ static VALUE
322
+ rbclipper_difference(int argc, VALUE* argv, VALUE self)
323
+ {
324
+ VALUE subjfill, clipfill, resulttype;
325
+ rb_scan_args(argc, argv, "03", &subjfill, &clipfill, &resulttype);
326
+ return rbclipper_execute_internal(self, ctDifference, subjfill, clipfill, resulttype);
327
+ }
328
+
329
+
330
+ static VALUE
331
+ rbclipper_xor(int argc, VALUE* argv, VALUE self)
332
+ {
333
+ VALUE subjfill, clipfill, resulttype;
334
+ rb_scan_args(argc, argv, "03", &subjfill, &clipfill, &resulttype);
335
+ return rbclipper_execute_internal(self, ctXor, subjfill, clipfill, resulttype);
336
+ }
337
+
338
+
339
+ typedef VALUE (*ruby_method)(...);
340
+
341
+ void Init_clipper() {
342
+ id_even_odd = rb_intern("even_odd");
343
+ id_non_zero = rb_intern("non_zero");
344
+ id_positive = rb_intern("positive");
345
+ id_negative = rb_intern("negative");
346
+ id_polygons = rb_intern("polygons");
347
+ id_ex_polygons = rb_intern("expolygons");
348
+ id_jtSquare = rb_intern("jtSquare");
349
+ id_jtMiter = rb_intern("jtMiter");
350
+ id_jtRound = rb_intern("jtRound");
351
+
352
+ VALUE mod = rb_define_module("Clipper");
353
+
354
+ VALUE k = rb_define_class_under(mod, "Clipper", rb_cObject);
355
+ rb_define_singleton_method(k, "new",
356
+ (ruby_method) rbclipper_new, 0);
357
+
358
+ rb_define_method(k, "orientation",
359
+ (ruby_method) rbclipper_orientation, 1);
360
+ rb_define_method(k, "area",
361
+ (ruby_method) rbclipper_area, 1);
362
+ rb_define_method(k, "offset_polygons",
363
+ (ruby_method) rbclipper_offset_polygons, -1);
364
+ rb_define_method(k, "add_subject_polygon",
365
+ (ruby_method) rbclipper_add_subject_polygon, 1);
366
+ rb_define_method(k, "add_clip_polygon",
367
+ (ruby_method) rbclipper_add_clip_polygon, 1);
368
+ rb_define_method(k, "add_subject_polygons",
369
+ (ruby_method) rbclipper_add_subject_polygons, 1);
370
+ rb_define_method(k, "add_clip_polygons",
371
+ (ruby_method) rbclipper_add_clip_polygons, 1);
372
+ rb_define_method(k, "clear!",
373
+ (ruby_method) rbclipper_clear, 0);
374
+ rb_define_method(k, "intersection",
375
+ (ruby_method) rbclipper_intersection, -1);
376
+ rb_define_method(k, "union",
377
+ (ruby_method) rbclipper_union, -1);
378
+ rb_define_method(k, "difference",
379
+ (ruby_method) rbclipper_difference, -1);
380
+ rb_define_method(k, "xor",
381
+ (ruby_method) rbclipper_xor, -1);
382
+
383
+ rb_define_method(k, "multiplier",
384
+ (ruby_method) rbclipper_multiplier, 0);
385
+ rb_define_method(k, "multiplier=",
386
+ (ruby_method) rbclipper_multiplier_eq, 1);
387
+ }
388
+
389
+ } /* extern "C" */
@@ -0,0 +1,6 @@
1
+ module Clipper
2
+ # Keep first two components sync'd to Clipper's major/minor.
3
+ # Last is our build number based on that. Hopefully there
4
+ # won't be too much churn re: Clipper's version vs. the bindings.
5
+ VERSION = "5.0.3"
6
+ end