rays 0.1.46 → 0.1.47

Sign up to get free protection for your applications and to get access to all the features.
data/src/polygon.cpp CHANGED
@@ -4,7 +4,7 @@
4
4
  #include <math.h>
5
5
  #include <assert.h>
6
6
  #include <utility>
7
- #include <poly2tri.h>
7
+ #include <earcut.hpp>
8
8
  #include <Splines.h>
9
9
  #include <xot/util.h>
10
10
  #include "rays/color.h"
@@ -17,21 +17,170 @@
17
17
  namespace clip = ClipperLib;
18
18
 
19
19
 
20
+ namespace mapbox
21
+ {
22
+
23
+ namespace util
24
+ {
25
+
26
+ template<>
27
+ struct nth<0, Rays::Point>
28
+ {
29
+ inline static auto get (const Rays::Point& p)
30
+ {
31
+ return p.x;
32
+ }
33
+ };
34
+
35
+ template<>
36
+ struct nth<1, Rays::Point>
37
+ {
38
+ inline static auto get (const Rays::Point& p)
39
+ {
40
+ return p.y;
41
+ }
42
+ };
43
+
44
+ }// util
45
+
46
+ }// mapbox
47
+
48
+
20
49
  namespace Rays
21
50
  {
22
51
 
23
52
 
24
- static inline p2t::Point
25
- to_poly2tri (const Point& point)
53
+ class Triangulator
26
54
  {
27
- return p2t::Point(point.x, point.y);
28
- }
29
55
 
30
- static inline Point
31
- from_poly2tri (const p2t::Point& point)
32
- {
33
- return Point(point.x, point.y);
34
- }
56
+ public:
57
+
58
+ Triangulator (size_t npoints)
59
+ {
60
+ points.reserve(npoints);
61
+ }
62
+
63
+ size_t begin (bool hole = false)
64
+ {
65
+ segments.emplace_back(points.size(), 0, hole);
66
+ return segments.back().begin;
67
+ }
68
+
69
+ void end (size_t n)
70
+ {
71
+ if (segments.empty())
72
+ invalid_state_error(__FILE__, __LINE__);
73
+
74
+ auto& seg = segments.back();
75
+ if (n != seg.begin)
76
+ argument_error(__FILE__, __LINE__);
77
+
78
+ seg.end = points.size();
79
+
80
+ if (seg.empty()) segments.pop_back();
81
+ }
82
+
83
+ void append (const Point& point)
84
+ {
85
+ points.emplace_back(point);
86
+ }
87
+
88
+ void triangulate ()
89
+ {
90
+ if (segments.empty()) return;
91
+
92
+ PolylineList polylines;
93
+ size_t index_offset = 0;
94
+ for (const auto& seg : segments)
95
+ {
96
+ Polyline polyline(&points[0] + seg.begin, seg.end - seg.begin);
97
+ if (!seg.hole)
98
+ {
99
+ triangulate(polylines, index_offset);
100
+ polylines.clear();
101
+ index_offset = seg.begin;
102
+ }
103
+ polylines.emplace_back(polyline);
104
+ }
105
+ triangulate(polylines, index_offset);
106
+
107
+ segments.clear();
108
+ segments.shrink_to_fit();
109
+ }
110
+
111
+ bool get_triangles (Polygon::TrianglePointList* triangles) const
112
+ {
113
+ if (indices.empty()) return false;
114
+
115
+ triangles->reserve(triangles->size() + indices.size());
116
+ for (const auto& index : indices)
117
+ triangles->emplace_back(points[index]);
118
+ return true;
119
+ }
120
+
121
+ private:
122
+
123
+ struct Segment
124
+ {
125
+
126
+ size_t begin, end;
127
+
128
+ bool hole;
129
+
130
+ Segment (size_t begin, size_t end, bool hole = false)
131
+ : begin(begin), end(end), hole(hole)
132
+ {
133
+ }
134
+
135
+ bool empty () const
136
+ {
137
+ return begin == end;
138
+ }
139
+
140
+ };// Segment
141
+
142
+ class Polyline
143
+ {
144
+
145
+ const Point* points;
146
+
147
+ size_t size_;
148
+
149
+ public:
150
+
151
+ typedef Point value_type;
152
+
153
+ Polyline (const Point* points, size_t size)
154
+ : points(points), size_(size)
155
+ {
156
+ }
157
+
158
+ size_t size () const {return size_;}
159
+
160
+ bool empty () const {return size_ == 0;}
161
+
162
+ const Point& operator [] (size_t i) const {return points[i];}
163
+
164
+ };// Polyline
165
+
166
+ typedef std::vector<Polyline> PolylineList;
167
+
168
+ std::vector<Segment> segments;
169
+
170
+ std::vector<Point> points;
171
+
172
+ std::vector<uint32_t> indices;
173
+
174
+ void triangulate (const PolylineList& polylines, size_t index_offset)
175
+ {
176
+ if (polylines.empty()) return;
177
+
178
+ auto result = mapbox::earcut<uint32_t>(polylines);
179
+ for (const auto& index : result)
180
+ indices.emplace_back(index_offset + index);
181
+ }
182
+
183
+ };// Triangulator
35
184
 
36
185
 
37
186
  struct Polygon::Data
@@ -39,16 +188,40 @@ namespace Rays
39
188
 
40
189
  LineList lines;
41
190
 
191
+ mutable std::unique_ptr<Bounds> pbounds;
192
+
193
+ mutable std::unique_ptr<Triangulator> ptriangulator;
194
+
42
195
  virtual ~Data ()
43
196
  {
44
197
  }
45
198
 
199
+ const Bounds& bounds () const
200
+ {
201
+ if (!pbounds)
202
+ {
203
+ if (lines.empty())
204
+ pbounds.reset(new Bounds(-1, -1, -1));
205
+ else
206
+ {
207
+ pbounds.reset(new Bounds(lines[0][0], 0));
208
+ for (const auto& line : lines)
209
+ {
210
+ for (const auto& point : line)
211
+ *pbounds |= point;
212
+ }
213
+ }
214
+ }
215
+ return *pbounds;
216
+ }
217
+
46
218
  void append (const Polyline& polyline, bool hole = false)
47
219
  {
48
220
  if (polyline.empty())
49
221
  return;
50
222
 
51
- lines.emplace_back(Line(polyline, hole));
223
+ lines.emplace_back(polyline, hole);
224
+ pbounds.reset();
52
225
  }
53
226
 
54
227
  void append (const Line& line)
@@ -57,48 +230,30 @@ namespace Rays
57
230
  return;
58
231
 
59
232
  lines.emplace_back(line);
233
+ pbounds.reset();
60
234
  }
61
235
 
62
236
  bool triangulate (TrianglePointList* triangles) const
63
237
  {
64
238
  assert(triangles);
65
239
 
66
- triangles->clear();
67
-
68
- if (!can_triangulate())
69
- return false;
70
-
71
- size_t npoint = count_points();
72
- if (npoint <= 0)
73
- return true;
74
-
75
- std::unique_ptr<p2t::CDT> cdt;
76
- std::vector<p2t::Point> points;
77
- std::vector<p2t::Point*> pointers;
78
-
79
- points.reserve(npoint);
80
- for (const auto& line : lines)
240
+ if (!ptriangulator)
81
241
  {
82
- pointers.clear();
83
- pointers.reserve(line.size());
84
- for (const auto& point : line)
85
- {
86
- points.emplace_back(to_poly2tri(point));
87
- pointers.emplace_back(&points.back());
88
- }
242
+ ptriangulator.reset(new Triangulator(count_points_for_triangulation()));
243
+ auto& t = *ptriangulator;
89
244
 
90
- if (!line.hole())
245
+ for (const auto& line : lines)
91
246
  {
92
- if (cdt) triangulate(triangles, cdt.get());
93
- cdt.reset(new p2t::CDT(pointers));
247
+ size_t n = t.begin(line.hole());
248
+ for (const auto& point : line)
249
+ t.append(point);
250
+ t.end(n);
94
251
  }
95
- else if (cdt)
96
- cdt->AddHole(pointers);
252
+ t.triangulate();
97
253
  }
98
254
 
99
- if (cdt) triangulate(triangles, cdt.get());
100
-
101
- return true;
255
+ triangles->clear();
256
+ return ptriangulator->get_triangles(triangles);
102
257
  }
103
258
 
104
259
  virtual void fill (Painter* painter, const Color& color) const = 0;
@@ -110,47 +265,35 @@ namespace Rays
110
265
 
111
266
  coord stroke_width = painter->stroke_width();
112
267
  if (stroke_width > 0)
113
- stroke_with_width(polygon, painter, color, stroke_width);
268
+ {
269
+ stroke_with_width(
270
+ polygon, painter, color, stroke_width, painter->stroke_outset());
271
+ }
114
272
  else
115
273
  stroke_without_width(painter, color);
116
274
  }
117
275
 
118
276
  private:
119
277
 
120
- bool can_triangulate () const
278
+ size_t count_points_for_triangulation () const
121
279
  {
280
+ size_t count = 0;
122
281
  for (const auto& line : lines)
123
282
  {
124
- if (line.loop() && !line.hole() && !line.empty())
125
- return true;
283
+ if (can_triangulate(line))
284
+ count += line.size();
126
285
  }
127
- return false;
128
- }
129
-
130
- size_t count_points () const
131
- {
132
- size_t count = 0;
133
- for (const auto& line : lines)
134
- count += line.size();
135
286
  return count;
136
287
  }
137
288
 
138
- void triangulate (TrianglePointList* triangles, p2t::CDT* cdt) const
289
+ bool can_triangulate (const Line& line) const
139
290
  {
140
- assert(triangles && cdt);
141
-
142
- cdt->Triangulate();
143
-
144
- for (auto* triangle : cdt->GetTriangles())
145
- {
146
- for (int i = 0; i < 3; ++i)
147
- triangles->emplace_back(from_poly2tri(*triangle->GetPoint(i)));
148
- }
291
+ return (line.fill() || line.hole()) && line.size() >= 3;
149
292
  }
150
293
 
151
294
  void stroke_with_width (
152
295
  const Polygon& polygon, Painter* painter,
153
- const Color& color, coord stroke_width) const
296
+ const Color& color, coord stroke_width, float stroke_outset) const
154
297
  {
155
298
  assert(painter && color && stroke_width > 0);
156
299
 
@@ -163,30 +306,45 @@ namespace Rays
163
306
  bool has_loop = false;
164
307
  for (const auto& polyline : polygon)
165
308
  {
166
- if (!polyline || polyline.empty())
167
- continue;
168
-
169
309
  if (polyline.loop())
170
310
  {
171
311
  has_loop = true;
172
312
  continue;
173
313
  }
314
+ stroke_polyline(polyline, painter, color, stroke_width, cap, join, ml);
315
+ }
174
316
 
175
- Polygon stroke;
176
- if (!polyline.expand(&stroke, stroke_width / 2, cap, join, ml))
177
- continue;
317
+ if (!has_loop) return;
178
318
 
179
- Polygon_fill(stroke, painter, color);
180
- }
319
+ coord inset = (-0.5 + stroke_outset) * stroke_width;
320
+ Polygon outline;
321
+ if (inset == 0)
322
+ outline = polygon;
323
+ else if (!polygon.expand(&outline, inset, cap, join, ml))
324
+ return;
181
325
 
182
- if (has_loop)
326
+ for (const auto& polyline : outline)
183
327
  {
184
- Polygon hole;
185
- if (polygon.expand(&hole, -stroke_width, cap, join, ml))
186
- Polygon_fill(polygon - hole, painter, color);
328
+ if (polyline.loop())
329
+ stroke_polyline(polyline, painter, color, stroke_width, cap, join, ml);
187
330
  }
188
331
  }
189
332
 
333
+ void stroke_polyline (
334
+ const Polyline& polyline, Painter* painter,
335
+ const Color& color, coord stroke_width,
336
+ CapType cap, JoinType join, coord miter_limit) const
337
+ {
338
+ assert(stroke_width > 0);
339
+
340
+ if (!polyline || polyline.empty())
341
+ return;
342
+
343
+ Polygon stroke;
344
+ if (polyline.expand(&stroke, stroke_width / 2, cap, join, miter_limit))
345
+ Polygon_fill(stroke, painter, color);
346
+ }
347
+
190
348
  void stroke_without_width (Painter* painter, const Color& color) const
191
349
  {
192
350
  assert(painter && color);
@@ -746,7 +904,7 @@ namespace Rays
746
904
 
747
905
  if (!has_hole)
748
906
  {
749
- points.emplace_back(Point(x + width / 2, y + height / 2));
907
+ points.emplace_back(x + width / 2, y + height / 2);
750
908
  mode = GL_TRIANGLE_FAN;
751
909
  }
752
910
 
@@ -788,6 +946,130 @@ namespace Rays
788
946
  };// EllipseData
789
947
 
790
948
 
949
+ static const char*
950
+ get_draw_mode_name (DrawMode mode)
951
+ {
952
+ switch (mode)
953
+ {
954
+ case DRAW_POINTS: return "DRAW_POINTS";
955
+ case DRAW_LINES: return "DRAW_LINES";
956
+ case DRAW_LINE_STRIP: return "DRAW_LINE_STRIP";
957
+ case DRAW_TRIANGLES: return "DRAW_TRIANGLES";
958
+ case DRAW_TRIANGLE_STRIP: return "DRAW_TRIANGLE_STRIP";
959
+ case DRAW_TRIANGLE_FAN: return "DRAW_TRIANGLE_FAN";
960
+ case DRAW_QUADS: return "DRAW_QUADS";
961
+ case DRAW_QUAD_STRIP: return "DRAW_QUAD_STRIP";
962
+ case DRAW_POLYGON: return "DRAW_POLYGON";
963
+ default: argument_error(__FILE__, __LINE__, "unknown draw mode");
964
+ }
965
+ }
966
+
967
+ static void
968
+ create_polygon (Polygon* polygon, const Point* points, size_t size, bool loop)
969
+ {
970
+ polygon->self->append(Polyline(points, size, loop, true));
971
+ }
972
+
973
+ static void
974
+ create_points (Polygon* polygon, const Point* points, size_t size)
975
+ {
976
+ for (size_t i = 0; i < size; ++i)
977
+ polygon->self->append(Polyline(&points[i], 1, false, false));
978
+ }
979
+
980
+ static void
981
+ create_lines (Polygon* polygon, const Point* points, size_t size)
982
+ {
983
+ for (size_t i = 0; i + 1 < size; i += 2)
984
+ polygon->self->append(Polyline(&points[i], 2, false, false));
985
+ }
986
+
987
+ static void
988
+ create_line_strip (
989
+ Polygon* polygon, const Point* points, size_t size, bool loop)
990
+ {
991
+ if (!loop || size < 3)
992
+ polygon->self->append(Polyline(points, size, loop, false));
993
+ else
994
+ {
995
+ std::vector<Point> array;
996
+ array.reserve(size + 1);
997
+ array.insert(array.begin(), points, points + size);
998
+ array.emplace_back(points[0]);
999
+ polygon->self->append(Polyline(&array[0], array.size(), true, false));
1000
+ }
1001
+ }
1002
+
1003
+ static void
1004
+ create_triangles (
1005
+ Polygon* polygon, const Point* points, size_t size, bool loop)
1006
+ {
1007
+ for (size_t i = 0; i + 2 < size; i += 3)
1008
+ polygon->self->append(Polyline(&points[i], 3, loop, true));
1009
+ }
1010
+
1011
+ static void
1012
+ create_triangle_strip (Polygon* polygon, const Point* points, size_t size)
1013
+ {
1014
+ if (size < 3) return;
1015
+
1016
+ size_t last = size - 1;
1017
+ size_t in_last = last % 2 == 0 ? last - 1 : last;
1018
+ size_t out_last = last % 2 == 0 ? last : last - 1;
1019
+
1020
+ std::vector<Point> array;
1021
+ array.emplace_back(points[0]);
1022
+ for (size_t i = 1; i <= in_last; i += 2)
1023
+ array.emplace_back(points[i]);
1024
+ for (size_t i = out_last; i >= 2; i -= 2)
1025
+ array.emplace_back(points[i]);
1026
+
1027
+ polygon->self->append(Polyline(&array[0], array.size(), true));
1028
+ if (size >= 4)
1029
+ polygon->self->append(Polyline(&points[1], size - 2));
1030
+ }
1031
+
1032
+ static void
1033
+ create_triangle_fan (Polygon* polygon, const Point* points, size_t size)
1034
+ {
1035
+ create_polygon(polygon, points, size, true);
1036
+
1037
+ Point array[2];
1038
+ array[0] = points[0];
1039
+ for (size_t i = 2; i < size - 1; ++i)
1040
+ {
1041
+ array[1] = points[i];
1042
+ polygon->self->append(Polyline(&array[0], 2));
1043
+ }
1044
+ }
1045
+
1046
+ static void
1047
+ create_quads (Polygon* polygon, const Point* points, size_t size, bool loop)
1048
+ {
1049
+ for (size_t i = 0; i + 3 < size; i += 4)
1050
+ polygon->self->append(Polyline(&points[i], 4, loop, true));
1051
+ }
1052
+
1053
+ static void
1054
+ create_quad_strip (Polygon* polygon, const Point* points, size_t size)
1055
+ {
1056
+ if (size < 4) return;
1057
+
1058
+ if (size % 2 != 0) --size;
1059
+ size_t in_last = size - 2;
1060
+ size_t out_last = size - 1;
1061
+
1062
+ std::vector<Point> array;
1063
+ for (size_t i = 0; i <= in_last; i += 2)
1064
+ array.emplace_back(points[i]);
1065
+ for (int i = (int) out_last; i >= 1; i -= 2)
1066
+ array.emplace_back(points[i]);
1067
+
1068
+ polygon->self->append(Polyline(&array[0], array.size(), true));
1069
+ for (size_t i = 2; i < in_last; i += 2)
1070
+ polygon->self->append(Polyline(&points[i], 2));
1071
+ }
1072
+
791
1073
  Polygon
792
1074
  create_line (coord x1, coord y1, coord x2, coord y2)
793
1075
  {
@@ -1069,7 +1351,30 @@ namespace Rays
1069
1351
  {
1070
1352
  if (!points || size <= 0) return;
1071
1353
 
1072
- self->append(Polyline(points, size, loop));
1354
+ create_polygon(this, points, size, loop);
1355
+ }
1356
+
1357
+ Polygon::Polygon (DrawMode mode, const Point* points, size_t size, bool loop)
1358
+ : self(new PolygonData())
1359
+ {
1360
+ if (!points || size <= 0) return;
1361
+
1362
+ switch (mode)
1363
+ {
1364
+ case DRAW_POINTS: create_points( this, points, size); break;
1365
+ case DRAW_LINES: create_lines( this, points, size); break;
1366
+ case DRAW_LINE_STRIP: create_line_strip( this, points, size, loop); break;
1367
+ case DRAW_TRIANGLES: create_triangles( this, points, size, loop); break;
1368
+ case DRAW_TRIANGLE_STRIP: create_triangle_strip(this, points, size); break;
1369
+ case DRAW_TRIANGLE_FAN: create_triangle_fan( this, points, size); break;
1370
+ case DRAW_QUADS: create_quads( this, points, size, loop); break;
1371
+ case DRAW_QUAD_STRIP: create_quad_strip( this, points, size); break;
1372
+ case DRAW_POLYGON: create_polygon( this, points, size, loop); break;
1373
+ default:
1374
+ argument_error(
1375
+ __FILE__, __LINE__,
1376
+ Xot::stringf("unknown draw mode '%s'", get_draw_mode_name(mode)));
1377
+ }
1073
1378
  }
1074
1379
 
1075
1380
  Polygon::Polygon (const Polyline& polyline)
@@ -1107,15 +1412,7 @@ namespace Rays
1107
1412
  Bounds
1108
1413
  Polygon::bounds () const
1109
1414
  {
1110
- if (empty()) return Bounds(-1, -1, -1);
1111
-
1112
- Bounds b(self->lines[0][0], 0);
1113
- for (const auto& line : *this)
1114
- {
1115
- for (const auto& point : line)
1116
- b |= point;
1117
- }
1118
- return b;
1415
+ return self->bounds();
1119
1416
  }
1120
1417
 
1121
1418
  size_t
@@ -1248,10 +1545,7 @@ namespace Rays
1248
1545
 
1249
1546
  Polygon::Line::operator bool () const
1250
1547
  {
1251
- if (!Super::operator bool())
1252
- return false;
1253
-
1254
- return loop() || !hole();
1548
+ return Super::operator bool() && (loop() || !hole());
1255
1549
  }
1256
1550
 
1257
1551
  bool
data/src/polyline.cpp CHANGED
@@ -17,21 +17,24 @@ namespace Rays
17
17
 
18
18
  PointList points;
19
19
 
20
- bool loop = false;
20
+ bool loop = false, fill = false;
21
21
 
22
22
  template <typename I, typename FUN>
23
- void reset (I begin, I end, bool loop_, FUN to_point_fun)
23
+ void reset (I begin, I end, bool loop_, bool fill_, FUN to_point_fun)
24
24
  {
25
- size_t size = end - begin;
26
- if (0 < size && size < 3 && loop_)
25
+ if (begin > end)
27
26
  argument_error(__FILE__, __LINE__);
28
27
 
29
28
  points.clear();
29
+ loop = loop_;
30
+ fill = fill_;
31
+
32
+ size_t size = end - begin;
33
+ if (size <= 0) return;
34
+
30
35
  points.reserve(size);
31
36
  for (auto it = begin; it != end; ++it)
32
37
  points.emplace_back(to_point_fun(*it));
33
-
34
- loop = loop_ && size > 0;
35
38
  }
36
39
 
37
40
  };// Polyline::Data
@@ -39,18 +42,16 @@ namespace Rays
39
42
 
40
43
  void
41
44
  Polyline_create (
42
- Polyline* polyline, const Path& path, bool loop, bool reverse)
45
+ Polyline* polyline, const Path& path, bool loop, bool hole)
43
46
  {
44
- assert(polyline);
45
-
46
47
  Path cleaned;
47
48
  ClipperLib::CleanPolygon(path, cleaned);
48
49
 
49
- auto to_point = [](const IntPoint& point) {return from_clipper(point);};
50
- if (reverse)
51
- polyline->self->reset(cleaned.rbegin(), cleaned.rend(), loop, to_point);
50
+ auto fun = [](const IntPoint& point) {return from_clipper(point);};
51
+ if (hole)
52
+ polyline->self->reset(cleaned.rbegin(), cleaned.rend(), loop, loop, fun);
52
53
  else
53
- polyline->self->reset(cleaned. begin(), cleaned. end(), loop, to_point);
54
+ polyline->self->reset(cleaned. begin(), cleaned. end(), loop, loop, fun);
54
55
  }
55
56
 
56
57
  template <typename I>
@@ -63,12 +64,12 @@ namespace Rays
63
64
  }
64
65
 
65
66
  void
66
- Polyline_get_path (Path* path, const Polyline& polyline, bool reverse)
67
+ Polyline_get_path (Path* path, const Polyline& polyline, bool hole)
67
68
  {
68
69
  assert(path);
69
70
 
70
71
  const auto& points = polyline.self->points;
71
- if (reverse)
72
+ if (hole)
72
73
  reset_path(path, points.rbegin(), points.rend());
73
74
  else
74
75
  reset_path(path, points. begin(), points. end());
@@ -81,7 +82,16 @@ namespace Rays
81
82
 
82
83
  Polyline::Polyline (const Point* points, size_t size, bool loop)
83
84
  {
84
- self->reset(points, points + size, loop, [](const Point& p) {return p;});
85
+ self->reset(
86
+ points, points + size, loop, loop,
87
+ [](const Point& p) {return p;});
88
+ }
89
+
90
+ Polyline::Polyline (const Point* points, size_t size, bool loop, bool fill)
91
+ {
92
+ self->reset(
93
+ points, points + size, loop, fill,
94
+ [](const Point& p) {return p;});
85
95
  }
86
96
 
87
97
  Polyline::~Polyline ()
@@ -114,6 +124,12 @@ namespace Rays
114
124
  return self->loop;
115
125
  }
116
126
 
127
+ bool
128
+ Polyline::fill () const
129
+ {
130
+ return self->fill;
131
+ }
132
+
117
133
  size_t
118
134
  Polyline::size () const
119
135
  {
@@ -146,8 +162,7 @@ namespace Rays
146
162
 
147
163
  Polyline::operator bool () const
148
164
  {
149
- size_t s = size();
150
- return !((s == 1 || s == 2) && self->loop);
165
+ return true;
151
166
  }
152
167
 
153
168
  bool