ruby_clipper 5.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Changelog +7 -0
- data/Gemfile +4 -0
- data/LICENSE.bindings +4 -0
- data/LICENSE.clipper +29 -0
- data/README.md +151 -0
- data/Rakefile +2 -0
- data/ext/clipper/clipper.cpp +3328 -0
- data/ext/clipper/clipper.hpp +306 -0
- data/ext/clipper/extconf.rb +6 -0
- data/ext/clipper/rbclipper.cpp +389 -0
- data/lib/clipper/version.rb +6 -0
- metadata +78 -0
@@ -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,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" */
|