clipper 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,247 @@
1
+ /*******************************************************************************
2
+ * *
3
+ * Author : Angus Johnson *
4
+ * Version : 2.9 *
5
+ * Date : 7 December 2010 *
6
+ * Copyright : Angus Johnson *
7
+ * *
8
+ * License: *
9
+ * Use, modification & distribution is subject to Boost Software License Ver 1. *
10
+ * http://www.boost.org/LICENSE_1_0.txt *
11
+ * *
12
+ * Attributions: *
13
+ * The code in this library is an extension of Bala Vatti's clipping algorithm: *
14
+ * "A generic solution to polygon clipping" *
15
+ * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
16
+ * http://portal.acm.org/citation.cfm?id=129906 *
17
+ * *
18
+ * Computer graphics and geometric modeling: implementation and algorithms *
19
+ * By Max K. Agoston *
20
+ * Springer; 1 edition (January 4, 2005) *
21
+ * Pages 98 - 106. *
22
+ * http://books.google.com/books?q=vatti+clipping+agoston *
23
+ * *
24
+ *******************************************************************************/
25
+
26
+ /*******************************************************************************
27
+ * *
28
+ * This is a translation of my Delphi clipper code and is the very first stuff *
29
+ * I've written in C++ (or C). My apologies if the coding style is unorthodox. *
30
+ * Please see the accompanying Delphi Clipper library (clipper.pas) for a more *
31
+ * detailed explanation of the code algorithms. *
32
+ * *
33
+ *******************************************************************************/
34
+
35
+ #pragma once
36
+ #ifndef clipper_hpp
37
+ #define clipper_hpp
38
+
39
+ #include <vector>
40
+ #include <string>
41
+
42
+ namespace clipper {
43
+
44
+ typedef enum _ClipType { ctIntersection, ctUnion, ctDifference, ctXor } TClipType;
45
+ typedef enum _PolyType { ptSubject, ptClip } TPolyType;
46
+ typedef enum _PolyFillType { pftEvenOdd, pftNonZero} TPolyFillType;
47
+
48
+ struct TDoublePoint { double X; double Y; };
49
+ struct TDoubleRect { double left; double top; double right; double bottom; };
50
+ typedef std::vector< TDoublePoint > TPolygon;
51
+ typedef std::vector< TPolygon > TPolyPolygon;
52
+
53
+ TDoublePoint DoublePoint(const double &X, const double &Y);
54
+ TPolyPolygon OffsetPolygons(const TPolyPolygon &pts, const double &delta);
55
+ double Area(const TPolygon &poly);
56
+ TDoubleRect GetBounds(const TPolygon &poly);
57
+ bool IsClockwise(const TPolygon &poly);
58
+
59
+ //used internally ...
60
+ typedef enum _EdgeSide { esLeft, esRight } TEdgeSide;
61
+ typedef enum _IntersectProtects { ipNone = 0,
62
+ ipLeft = 1, ipRight = 2, ipBoth = 3 } TIntersectProtects;
63
+ typedef enum _TriState { sFalse, sTrue, sUndefined} TTriState;
64
+
65
+ struct TEdge {
66
+ double x;
67
+ double y;
68
+ double xbot;
69
+ double ybot;
70
+ double xtop;
71
+ double ytop;
72
+ double dx;
73
+ double tmpX;
74
+ bool nextAtTop;
75
+ TPolyType polyType;
76
+ TEdgeSide side;
77
+ int windDelta; //1 or -1 depending on winding direction
78
+ int windCnt;
79
+ int windCnt2; //winding count of the opposite polytype
80
+ int outIdx;
81
+ TEdge *next;
82
+ TEdge *prev;
83
+ TEdge *nextInLML;
84
+ TEdge *nextInAEL;
85
+ TEdge *prevInAEL;
86
+ TEdge *nextInSEL;
87
+ TEdge *prevInSEL;
88
+ };
89
+
90
+ struct TIntersectNode {
91
+ TEdge *edge1;
92
+ TEdge *edge2;
93
+ TDoublePoint pt;
94
+ TIntersectNode *next;
95
+ TIntersectNode *prev;
96
+ };
97
+
98
+ struct TLocalMinima {
99
+ double Y;
100
+ TEdge *leftBound;
101
+ TEdge *rightBound;
102
+ TLocalMinima *nextLm;
103
+ };
104
+
105
+ struct TScanbeam {
106
+ double Y;
107
+ TScanbeam *nextSb;
108
+ };
109
+
110
+ struct TPolyPt {
111
+ TDoublePoint pt;
112
+ TPolyPt *next;
113
+ TPolyPt *prev;
114
+ TTriState isHole;
115
+ };
116
+
117
+ struct TJoinRec {
118
+ TDoublePoint pt;
119
+ int idx1;
120
+ union {
121
+ int idx2;
122
+ TPolyPt* outPPt; //horiz joins only
123
+ };
124
+ };
125
+
126
+ typedef std::vector < TPolyPt * > PolyPtList;
127
+ typedef std::vector < TJoinRec > JoinList;
128
+
129
+ //ClipperBase is the ancestor to the Clipper class. It should not be
130
+ //instantiated directly. This class simply abstracts the conversion of sets of
131
+ //polygon coordinates into edge objects that are stored in a LocalMinima list.
132
+ class ClipperBase
133
+ {
134
+ public:
135
+ ClipperBase();
136
+ virtual ~ClipperBase();
137
+ void AddPolygon(const TPolygon &pg, TPolyType polyType);
138
+ void AddPolyPolygon( const TPolyPolygon &ppg, TPolyType polyType);
139
+ virtual void Clear();
140
+ TDoubleRect GetBounds();
141
+ protected:
142
+ void DisposeLocalMinimaList();
143
+ void InsertLocalMinima(TLocalMinima *newLm);
144
+ TEdge* AddBoundsToLML(TEdge *e);
145
+ void PopLocalMinima();
146
+ bool Reset();
147
+ TLocalMinima *m_CurrentLM;
148
+ private:
149
+ TLocalMinima *m_localMinimaList;
150
+ std::vector< TEdge * > m_edges;
151
+ };
152
+
153
+ class Clipper : public virtual ClipperBase
154
+ {
155
+ public:
156
+ Clipper();
157
+ ~Clipper();
158
+ bool Execute(TClipType clipType,
159
+ TPolyPolygon &solution,
160
+ TPolyFillType subjFillType = pftEvenOdd,
161
+ TPolyFillType clipFillType = pftEvenOdd);
162
+ //The ForceOrientation property ensures that polygons that result from a
163
+ //TClipper.Execute() calls will have clockwise 'outer' and counter-clockwise
164
+ //'inner' (or 'hole') polygons. If ForceOrientation == false, then the
165
+ //polygons returned in the solution will have undefined orientation.<br>
166
+ //Setting ForceOrientation = true results in a minor penalty (~10%) in
167
+ //execution speed. (Default == true) ***DEPRICATED***
168
+ bool ForceOrientation();
169
+ void ForceOrientation(bool value);
170
+ private:
171
+ PolyPtList m_PolyPts;
172
+ JoinList m_Joins;
173
+ JoinList m_CurrentHorizontals;
174
+ TClipType m_ClipType;
175
+ TScanbeam *m_Scanbeam;
176
+ TEdge *m_ActiveEdges;
177
+ TEdge *m_SortedEdges;
178
+ TIntersectNode *m_IntersectNodes;
179
+ bool m_ExecuteLocked;
180
+ bool m_ForceOrientation;
181
+ TPolyFillType m_ClipFillType;
182
+ TPolyFillType m_SubjFillType;
183
+ double m_IntersectTolerance;
184
+ void UpdateHoleStates();
185
+ void DisposeScanbeamList();
186
+ void SetWindingDelta(TEdge *edge);
187
+ void SetWindingCount(TEdge *edge);
188
+ bool IsNonZeroFillType(TEdge *edge);
189
+ bool IsNonZeroAltFillType(TEdge *edge);
190
+ bool InitializeScanbeam();
191
+ void InsertScanbeam( const double &Y);
192
+ double PopScanbeam();
193
+ void InsertLocalMinimaIntoAEL( const double &botY);
194
+ void InsertEdgeIntoAEL(TEdge *edge);
195
+ void AddEdgeToSEL(TEdge *edge);
196
+ void CopyAELToSEL();
197
+ void DeleteFromSEL(TEdge *e);
198
+ void DeleteFromAEL(TEdge *e);
199
+ void UpdateEdgeIntoAEL(TEdge *&e);
200
+ void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
201
+ bool Process1Before2(TIntersectNode *Node1, TIntersectNode *Node2);
202
+ bool TestIntersections();
203
+ bool IsContributing(TEdge *edge);
204
+ bool IsTopHorz(TEdge *horzEdge, const double &XPos);
205
+ void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
206
+ void DoMaxima(TEdge *e, const double &topY);
207
+ void ProcessHorizontals();
208
+ void ProcessHorizontal(TEdge *horzEdge);
209
+ void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const TDoublePoint &pt);
210
+ void AddLocalMinPoly(TEdge *e1, TEdge *e2, const TDoublePoint &pt);
211
+ void AppendPolygon(TEdge *e1, TEdge *e2);
212
+ void DoEdge1(TEdge *edge1, TEdge *edge2, const TDoublePoint &pt);
213
+ void DoEdge2(TEdge *edge1, TEdge *edge2, const TDoublePoint &pt);
214
+ void DoBothEdges(TEdge *edge1, TEdge *edge2, const TDoublePoint &pt);
215
+ void IntersectEdges(TEdge *e1, TEdge *e2,
216
+ const TDoublePoint &pt, TIntersectProtects protects);
217
+ TPolyPt* AddPolyPt(TEdge *e, const TDoublePoint &pt);
218
+ TPolyPt* InsertPolyPtBetween(const TDoublePoint &pt, TPolyPt* pp1, TPolyPt* pp2);
219
+ void DisposeAllPolyPts();
220
+ void ProcessIntersections( const double &topY);
221
+ void AddIntersectNode(TEdge *e1, TEdge *e2, const TDoublePoint &pt);
222
+ void BuildIntersectList(const double &topY);
223
+ void ProcessIntersectList();
224
+ TEdge *BubbleSwap(TEdge *edge);
225
+ void ProcessEdgesAtTopOfScanbeam( const double &topY);
226
+ void BuildResult(TPolyPolygon &polypoly);
227
+ void DisposeIntersectNodes();
228
+ void FixupJoins(int oldIdx, int newIdx);
229
+ void MergePolysWithCommonEdges();
230
+ void FixupJoins(int joinIdx);
231
+ };
232
+
233
+ class clipperException : public std::exception
234
+ {
235
+ public:
236
+ clipperException(const char* description = "Clipper exception")
237
+ throw(): std::exception(), m_description (description) {}
238
+ virtual ~clipperException() throw() {}
239
+ virtual const char* what() const throw() {return m_description.c_str();}
240
+ private:
241
+ std::string m_description;
242
+ };
243
+
244
+ } //clipper namespace
245
+ #endif //clipper_hpp
246
+
247
+
@@ -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,268 @@
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 clipper;
18
+
19
+ static ID id_even_odd;
20
+ static ID id_non_zero;
21
+
22
+ static inline Clipper*
23
+ XCLIPPER(VALUE x)
24
+ {
25
+ Clipper* clipper;
26
+ Data_Get_Struct(x, Clipper, clipper);
27
+ return clipper;
28
+ }
29
+
30
+ static inline TPolyFillType
31
+ sym_to_filltype(VALUE sym)
32
+ {
33
+ ID inp = rb_to_id(sym);
34
+
35
+ if (inp == id_even_odd) {
36
+ return pftEvenOdd;
37
+ } else if (inp == id_non_zero) {
38
+ return pftNonZero;
39
+ }
40
+
41
+ rb_raise(rb_eArgError, "%s", "Expected :even_odd or :non_zero");
42
+ }
43
+
44
+ extern "C" {
45
+
46
+ static void
47
+ ary_to_polygon(VALUE ary, TPolygon* poly)
48
+ {
49
+ const char* earg =
50
+ "Polygons have format: [[p0_x, p0_y], [p1_x, p1_y], ...]";
51
+
52
+ Check_Type(ary, T_ARRAY);
53
+
54
+ for(long i = 0; i != RARRAY_LEN(ary); i++) {
55
+ VALUE sub = rb_ary_entry(ary, i);
56
+ Check_Type(sub, T_ARRAY);
57
+
58
+ if(RARRAY_LEN(sub) != 2) {
59
+ rb_raise(rb_eArgError, "%s", earg);
60
+ }
61
+
62
+ VALUE px = rb_ary_entry(sub, 0);
63
+ VALUE py = rb_ary_entry(sub, 1);
64
+ poly->push_back(DoublePoint(NUM2DBL(px), NUM2DBL(py)));
65
+ }
66
+ }
67
+
68
+ static void
69
+ ary_to_polypolygon(VALUE ary, TPolyPolygon* polypoly)
70
+ {
71
+ Check_Type(ary, T_ARRAY);
72
+ for(long i = 0; i != RARRAY_LEN(ary); i++) {
73
+ TPolygon p;
74
+ VALUE sub = rb_ary_entry(ary, i);
75
+ Check_Type(sub, T_ARRAY);
76
+ ary_to_polygon(sub, &p);
77
+ polypoly->push_back(p);
78
+ }
79
+ }
80
+
81
+ static void
82
+ rbclipper_free(void* ptr)
83
+ {
84
+ delete (Clipper*) ptr;
85
+ }
86
+
87
+ static VALUE
88
+ rbclipper_new(VALUE klass)
89
+ {
90
+ Clipper* ptr = new Clipper;
91
+ VALUE r = Data_Wrap_Struct(klass, 0, rbclipper_free, ptr);
92
+ rb_obj_call_init(r, 0, 0);
93
+ return r;
94
+ }
95
+
96
+ static VALUE
97
+ rbclipper_add_polygon_internal(VALUE self, VALUE polygon,
98
+ TPolyType polytype)
99
+ {
100
+ TPolygon tmp;
101
+ ary_to_polygon(polygon, &tmp);
102
+ XCLIPPER(self)->AddPolygon(tmp, polytype);
103
+ return Qnil;
104
+ }
105
+
106
+ static VALUE
107
+ rbclipper_add_subject_polygon(VALUE self, VALUE polygon)
108
+ {
109
+ return rbclipper_add_polygon_internal(self, polygon, ptSubject);
110
+ }
111
+
112
+ static VALUE
113
+ rbclipper_add_clip_polygon(VALUE self, VALUE polygon)
114
+ {
115
+ return rbclipper_add_polygon_internal(self, polygon, ptClip);
116
+ }
117
+
118
+
119
+ static VALUE
120
+ rbclipper_add_poly_polygon_internal(VALUE self, VALUE polypoly,
121
+ TPolyType polytype)
122
+ {
123
+ TPolyPolygon tmp;
124
+ ary_to_polypolygon(polypoly, &tmp);
125
+ XCLIPPER(self)->AddPolyPolygon(tmp, polytype);
126
+ return Qnil;
127
+ }
128
+
129
+ static VALUE
130
+ rbclipper_add_subject_poly_polygon(VALUE self, VALUE polygon)
131
+ {
132
+ return rbclipper_add_poly_polygon_internal(self, polygon, ptSubject);
133
+ }
134
+
135
+ static VALUE
136
+ rbclipper_add_clip_poly_polygon(VALUE self, VALUE polygon)
137
+ {
138
+ return rbclipper_add_poly_polygon_internal(self, polygon, ptClip);
139
+ }
140
+
141
+
142
+ static VALUE
143
+ rbclipper_clear(VALUE self)
144
+ {
145
+ XCLIPPER(self)->Clear();
146
+ return Qnil;
147
+ }
148
+
149
+ static VALUE
150
+ rbclipper_force_orientation(VALUE self)
151
+ {
152
+ return XCLIPPER(self)->ForceOrientation() ? Qtrue : Qfalse;
153
+ }
154
+
155
+ static VALUE
156
+ rbclipper_force_orientation_eq(VALUE self, VALUE b)
157
+ {
158
+ XCLIPPER(self)->ForceOrientation(b == Qtrue);
159
+ return b;
160
+ }
161
+
162
+ static VALUE
163
+ rbclipper_execute_internal(VALUE self, TClipType cliptype,
164
+ VALUE subjfill, VALUE clipfill)
165
+ {
166
+ if (NIL_P(subjfill))
167
+ subjfill = ID2SYM(id_even_odd);
168
+
169
+ if (NIL_P(clipfill))
170
+ clipfill = ID2SYM(id_even_odd);
171
+
172
+ TPolyPolygon solution;
173
+ XCLIPPER(self)->Execute((TClipType) cliptype,
174
+ solution,
175
+ sym_to_filltype(subjfill),
176
+ sym_to_filltype(clipfill));
177
+ VALUE r = rb_ary_new();
178
+ for(TPolyPolygon::iterator i = solution.begin();
179
+ i != solution.end();
180
+ ++i) {
181
+ VALUE sub = rb_ary_new();
182
+ for(TPolygon::iterator p = i->begin(); p != i->end(); ++p) {
183
+ rb_ary_push(sub, rb_ary_new3(2, DBL2NUM(p->X), DBL2NUM(p->Y)));
184
+ }
185
+ rb_ary_push(r, sub);
186
+ }
187
+
188
+ return r;
189
+ }
190
+
191
+ static VALUE
192
+ rbclipper_intersection(int argc, VALUE* argv, VALUE self)
193
+ {
194
+ VALUE subjfill, clipfill;
195
+ rb_scan_args(argc, argv, "02", &subjfill, &clipfill);
196
+ return rbclipper_execute_internal(self, ctIntersection, subjfill, clipfill);
197
+ }
198
+
199
+ static VALUE
200
+ rbclipper_union(int argc, VALUE* argv, VALUE self)
201
+ {
202
+ VALUE subjfill, clipfill;
203
+
204
+ rb_scan_args(argc, argv, "02", &subjfill, &clipfill);
205
+
206
+ return rbclipper_execute_internal(self, ctUnion, subjfill, clipfill);
207
+ }
208
+
209
+
210
+ static VALUE
211
+ rbclipper_difference(int argc, VALUE* argv, VALUE self)
212
+ {
213
+ VALUE subjfill, clipfill;
214
+ rb_scan_args(argc, argv, "02", &subjfill, &clipfill);
215
+ return rbclipper_execute_internal(self, ctDifference, subjfill, clipfill);
216
+ }
217
+
218
+
219
+ static VALUE
220
+ rbclipper_xor(int argc, VALUE* argv, VALUE self)
221
+ {
222
+ VALUE subjfill, clipfill;
223
+ rb_scan_args(argc, argv, "02", &subjfill, &clipfill);
224
+ return rbclipper_execute_internal(self, ctXor, subjfill, clipfill);
225
+ }
226
+
227
+
228
+ typedef VALUE (*ruby_method)(...);
229
+
230
+ void Init_clipper() {
231
+ id_even_odd = rb_intern("even_odd");
232
+ id_non_zero = rb_intern("non_zero");
233
+
234
+ VALUE mod = rb_define_module("Clipper");
235
+
236
+ VALUE k = rb_define_class_under(mod, "Clipper", rb_cObject);
237
+ rb_define_singleton_method(k, "new",
238
+ (ruby_method) rbclipper_new, 0);
239
+
240
+ rb_define_method(k, "add_subject_polygon",
241
+ (ruby_method) rbclipper_add_subject_polygon, 1);
242
+ rb_define_method(k, "add_clip_polygon",
243
+ (ruby_method) rbclipper_add_clip_polygon, 1);
244
+
245
+ rb_define_method(k, "add_subject_poly_polygon",
246
+ (ruby_method) rbclipper_add_subject_poly_polygon, 1);
247
+ rb_define_method(k, "add_clip_poly_polygon",
248
+ (ruby_method) rbclipper_add_clip_poly_polygon, 1);
249
+
250
+
251
+ rb_define_method(k, "clear!",
252
+ (ruby_method) rbclipper_clear, 0);
253
+ rb_define_method(k, "force_orientation",
254
+ (ruby_method) rbclipper_force_orientation, 0);
255
+ rb_define_method(k, "force_orientation=",
256
+ (ruby_method) rbclipper_force_orientation_eq, 1);
257
+
258
+ rb_define_method(k, "intersection",
259
+ (ruby_method) rbclipper_intersection, -1);
260
+ rb_define_method(k, "union",
261
+ (ruby_method) rbclipper_union, -1);
262
+ rb_define_method(k, "difference",
263
+ (ruby_method) rbclipper_difference, -1);
264
+ rb_define_method(k, "xor",
265
+ (ruby_method) rbclipper_xor, -1);
266
+ }
267
+
268
+ } /* extern "C" */