clipper 2.9.0

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