rays 0.1.46 → 0.1.47

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.
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