contrek 1.1.2 → 1.1.4

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +11 -1
  4. data/Gemfile.lock +1 -1
  5. data/LICENSE-MIT.md +9 -0
  6. data/README.md +58 -17
  7. data/contrek.gemspec +0 -1
  8. data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +14 -2
  9. data/ext/cpp_polygon_finder/PolygonFinder/LICENSE_AGPL.txt +661 -0
  10. data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +9 -7
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +9 -0
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +4 -0
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +13 -2
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/List.cpp +74 -82
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/List.h +12 -4
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Lists.cpp +0 -10
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Lists.h +0 -7
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +64 -46
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +14 -23
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +39 -44
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.h +5 -7
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +6 -4
  23. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +4 -3
  24. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +12 -4
  25. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/EndPoint.h +3 -2
  26. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +1 -0
  27. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +35 -2
  28. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +5 -1
  29. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +11 -11
  30. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +2 -1
  31. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +16 -2
  32. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +2 -0
  33. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +2 -5
  34. data/ext/cpp_polygon_finder/extconf.rb +12 -2
  35. data/lib/contrek/bitmaps/painting.rb +1 -0
  36. data/lib/contrek/finder/concurrent/cursor.rb +7 -5
  37. data/lib/contrek/finder/concurrent/finder.rb +2 -1
  38. data/lib/contrek/finder/concurrent/part.rb +12 -3
  39. data/lib/contrek/finder/concurrent/partitionable.rb +7 -5
  40. data/lib/contrek/finder/node.rb +61 -32
  41. data/lib/contrek/finder/node_cluster.rb +30 -27
  42. data/lib/contrek/finder/polygon_finder.rb +4 -4
  43. data/lib/contrek/version.rb +1 -1
  44. metadata +5 -4
@@ -14,6 +14,7 @@
14
14
  #include <string>
15
15
  #include <vector>
16
16
  #include <utility>
17
+ #include <deque>
17
18
  #include "NodeCluster.h"
18
19
  #include "Node.h"
19
20
  #include "RectBounds.h"
@@ -30,15 +31,9 @@ NodeCluster::NodeCluster(int h, int w, pf_Options *options) {
30
31
  this->nodes = 0;
31
32
 
32
33
  this->vert_nodes.resize(h);
33
- for (int i = 0; i < h; ++i) {
34
- this->vert_nodes[i].reserve(w/2);
35
- }
36
-
37
34
  this->root_nodes = this->lists.add_list();
38
35
  this->inner_plot = this->lists.add_list();
39
36
  this->inner_new = this->lists.add_list();
40
-
41
- this->points.reserve(w * h);
42
37
  }
43
38
 
44
39
  NodeCluster::~NodeCluster() {
@@ -74,32 +69,33 @@ void NodeCluster::compress_coords(std::list<Polygon>& polygons, pf_Options optio
74
69
  void NodeCluster::build_tangs_sequence() {
75
70
  for (auto& line : vert_nodes) {
76
71
  for (Node& node : line) {
77
- node.precalc_tangs_sequences(this->points);
72
+ node.precalc_tangs_sequences(*this);
78
73
  }
79
74
  }
80
75
  }
81
76
 
82
- Node* NodeCluster::add_node(int min_x, int max_x, int y, char name) {
83
- vert_nodes[y].emplace_back(min_x, max_x, y, name);
77
+ Node* NodeCluster::add_node(int min_x, int max_x, int y, char name, int offset) {
78
+ vert_nodes[y].emplace_back(min_x, max_x, y, this, name);
84
79
 
85
80
  Node& node = vert_nodes[y].back();
86
- node.data_pointer = this->lists.get_data_pointer();
87
81
  node.abs_x_index = vert_nodes[y].size() - 1;
88
82
  this->nodes++;
89
83
 
90
84
  root_nodes->push_back(&node);
91
85
 
92
86
  if (y > 0) {
93
- std::vector<Node>& up_nodes = vert_nodes[y - 1];
87
+ std::deque<Node>& up_nodes = vert_nodes[y - 1];
94
88
  if (!up_nodes.empty()) {
95
89
  auto it = std::lower_bound(up_nodes.begin(), up_nodes.end(), node.min_x,
96
- [](const Node& a, int val) {
97
- return a.max_x < val;
90
+ [&](const Node& a, int val) {
91
+ return ((a.max_x + offset) < val);
98
92
  });
93
+
99
94
  while (it != up_nodes.end()) {
100
- if (it->min_x > node.max_x) break;
101
- node.add_intersection(&(*it));
102
- it->add_intersection(&node);
95
+ if ((it->min_x - offset) > node.max_x) break;
96
+ int current_index = std::distance(up_nodes.begin(), it);
97
+ node.add_intersection(*it, current_index);
98
+ it->add_intersection(node, node.abs_x_index);
103
99
  ++it;
104
100
  }
105
101
  }
@@ -119,8 +115,11 @@ void NodeCluster::plot(int versus) {
119
115
  this->plot_sequence.push_back(root_node);
120
116
  Polygon poly;
121
117
 
122
- if ((root_node)->tangs_sequence.size() > 0) // front() on empty list is undefined
123
- { versus == Node::A ? next_node = root_node->tangs_sequence.back().node : next_node = root_node->tangs_sequence.front().node;
118
+ if ((root_node)->tangs_sequence.size() > 0) // front() or back() on empty list is undefined
119
+ { versus == Node::A ?
120
+ next_node = root_node->get_tangent_node_by_virtual_index(root_node->tangs_sequence.back()) :
121
+ next_node = root_node->get_tangent_node_by_virtual_index(root_node->tangs_sequence.front());
122
+
124
123
  if (next_node != nullptr)
125
124
  { Point* p = next_node->coords_entering_to(root_node, versus_inverter[versus], Node::OUTER);
126
125
  poly.outer.push_back(p);
@@ -159,13 +158,20 @@ void NodeCluster::plot(int versus) {
159
158
 
160
159
  first->inner_index = index_inner;
161
160
 
162
- Node *next_node;
163
- if (first->get_trackmax())
164
- { if (inner_v == Node::A) next_node = first->tangs[Node::T_UP].front();
165
- else next_node = first->tangs[Node::T_DOWN].front();
161
+ Node* next_node = nullptr;
162
+
163
+ if (first->get_trackmax()) {
164
+ if (inner_v == Node::A) {
165
+ if (first->upper_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_UP][first->upper_start];
166
+ } else {
167
+ if (first->lower_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_DOWN][first->lower_start];
168
+ }
166
169
  } else {
167
- if (inner_v == Node::A) next_node = first->tangs[Node::T_DOWN].back();
168
- else next_node = first->tangs[Node::T_UP].back();
170
+ if (inner_v == Node::A) {
171
+ if (first->lower_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_DOWN][first->lower_end];
172
+ } else {
173
+ if (first->upper_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_UP][first->upper_end];
174
+ }
169
175
  }
170
176
 
171
177
  if (next_node != nullptr)
@@ -251,7 +257,11 @@ void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *no
251
257
 
252
258
  bool plot = true;
253
259
  if (next_node->y == last_node->y) {
254
- Node *n = (versus == Node::A ? current_node->tangs_sequence.front().node : current_node->tangs_sequence.back().node);
260
+ Node *n;
261
+ int virtual_index = (versus == Node::A ?
262
+ current_node->tangs_sequence.front() :
263
+ current_node->tangs_sequence.back());
264
+ n = current_node->get_tangent_node_by_virtual_index(virtual_index);
255
265
  plot = (n == next_node);
256
266
  }
257
267
  if (plot) {
@@ -286,7 +296,10 @@ void NodeCluster::plot_node(std::vector<Point*>& sequence_coords, Node *node, No
286
296
 
287
297
  bool plot = true;
288
298
  if (next_node->y == last_node->y) {
289
- Node *n = (versus == Node::A ? current_node->tangs_sequence.back().node : current_node->tangs_sequence.front().node);
299
+ int virtual_index = (versus == Node::A ?
300
+ current_node->tangs_sequence.back() :
301
+ current_node->tangs_sequence.front());
302
+ Node *n = current_node->get_tangent_node_by_virtual_index(virtual_index);
290
303
  plot = (n == next_node);
291
304
  }
292
305
  if (plot) {
@@ -309,21 +322,3 @@ void NodeCluster::plot_node(std::vector<Point*>& sequence_coords, Node *node, No
309
322
  current_node = next_node;
310
323
  }
311
324
  }
312
-
313
- void NodeCluster::list_track(Node *node, std::list<Node*> *list) {
314
- std::list<Node*>::iterator i = std::find(list->begin(), list->end(), node);
315
- if (i == list->end()) list->push_back(node);
316
- else list_delete(node, list);
317
- }
318
-
319
- void NodeCluster::list_delete(Node *node, std::list<Node*> *list) {
320
- std::list<Node*>::iterator i;
321
- while ((i = std::find(list->begin(), list->end(), node)) != list->end())
322
- { list->erase(i);
323
- }
324
- }
325
-
326
- bool NodeCluster::list_present(Node *node, std::list<Node*> *list) {
327
- std::list<Node*>::iterator i = std::find(list->begin(), list->end(), node);
328
- return(!(i == list->end()));
329
- }
@@ -10,6 +10,7 @@
10
10
  #pragma once
11
11
  #include <list>
12
12
  #include <vector>
13
+ #include <deque>
13
14
  #include <map>
14
15
  #include <string>
15
16
  #include <utility>
@@ -32,24 +33,21 @@ class NodeCluster {
32
33
  int versus_inverter[2];
33
34
  int count = 0;
34
35
  int nodes;
35
- std::vector<Point> points;
36
+ int width;
36
37
 
37
38
  public:
38
39
  pf_Options *options;
39
40
  std::vector<std::pair<int, int>> treemap; // [a,b] a = index of parent outer, b = index of inner of parent outer
40
41
  std::pair<int, int> test_in_hole_a(Node* node);
41
42
  std::pair<int, int> test_in_hole_o(Node* node);
42
- std::vector<std::vector<Node>> vert_nodes;
43
- void list_track(Node *node, std::list<Node*> *list);
44
- void list_delete(Node *node, std::list<Node*> *list);
45
- bool list_present(Node *node, std::list<Node*> *list);
43
+ std::vector<std::deque<Node>> vert_nodes;
46
44
  void compress_coords(std::list<Polygon>& polygons, pf_Options options);
47
45
  List *root_nodes;
48
- int height, width;
46
+ int height;
49
47
  std::list<Polygon> polygons;
50
48
  NodeCluster(int h, int w, pf_Options *options);
51
49
  virtual ~NodeCluster();
52
- Node* add_node(int min_x, int max_x, int y, char name);
50
+ Node* add_node(int min_x, int max_x, int y, char name, int offset);
53
51
  void calc_root_nodes();
54
52
  void build_tangs_sequence();
55
53
  void plot(int versus);
@@ -37,6 +37,7 @@ PolygonFinder::PolygonFinder(Bitmap *bitmap,
37
37
  end_x(end_x == -1 ? bitmap->w() : end_x)
38
38
  { this->rgb_matcher = dynamic_cast<RGBMatcher*>(matcher);
39
39
  if (options != nullptr) FinderUtils::sanitize_options(this->options, options);
40
+
40
41
  this->node_cluster = new NodeCluster(source_bitmap->h(), source_bitmap->w(), &this->options);
41
42
 
42
43
  //= SCAN ==============//
@@ -82,17 +83,18 @@ PolygonFinder::~PolygonFinder() {
82
83
 
83
84
  void PolygonFinder::scan() {
84
85
  int bpp = this->source_bitmap->get_bytes_per_pixel();
86
+ int offset = options.connectivity_offset;
85
87
 
86
88
  RGBMatcher* rgb_m = dynamic_cast<RGBMatcher*>(this->matcher);
87
89
 
88
90
  if (bpp == 1) {
89
91
  auto fetcher = [](const unsigned char* p) { return static_cast<unsigned int>(p[0]); };
90
- if (rgb_m) run_loop(rgb_m, fetcher);
91
- else run_loop(this->matcher, fetcher);
92
+ if (rgb_m) run_loop(rgb_m, fetcher, offset);
93
+ else run_loop(this->matcher, fetcher, offset);
92
94
  } else {
93
95
  auto fetcher = [](const unsigned char* p) { return *reinterpret_cast<const uint32_t*>(p); };
94
- if (rgb_m) run_loop(rgb_m, fetcher);
95
- else run_loop(this->matcher, fetcher);
96
+ if (rgb_m) run_loop(rgb_m, fetcher, offset);
97
+ else run_loop(this->matcher, fetcher, offset);
96
98
  }
97
99
  }
98
100
 
@@ -41,6 +41,7 @@ struct pf_Options {
41
41
  bool compress_visvalingam = false;
42
42
  bool named_sequences = false;
43
43
  bool bounds = false;
44
+ int connectivity_offset = 0;
44
45
  float compress_visvalingam_tolerance = 10.0;
45
46
  int number_of_tiles = 1;
46
47
  std::string get_alpha_versus() {
@@ -94,7 +95,7 @@ class PolygonFinder {
94
95
  CpuTimer cpu_timer;
95
96
 
96
97
  template <typename M, typename F>
97
- void run_loop(M* specific_matcher, F&& fetch_color) {
98
+ void run_loop(M* specific_matcher, F&& fetch_color, int offset) {
98
99
  int img_h = this->source_bitmap->h();
99
100
  int bpp = this->source_bitmap->get_bytes_per_pixel();
100
101
  for (int y = 0; y < img_h; y++) {
@@ -114,11 +115,11 @@ class PolygonFinder {
114
115
  matching = true;
115
116
  }
116
117
  if (x == this->end_x - 1) {
117
- this->node_cluster->add_node(min_x, x, y, last_red_value);
118
+ this->node_cluster->add_node(min_x, x, y, last_red_value, offset);
118
119
  matching = false;
119
120
  }
120
121
  } else if (matching) {
121
- this->node_cluster->add_node(min_x, x - 1, y, last_red_value);
122
+ this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
122
123
  matching = false;
123
124
  }
124
125
  }
@@ -58,7 +58,8 @@ void Cursor::traverse_outer(Part* act_part,
58
58
  std::vector<Shape*>& shapes_sequence,
59
59
  Sequence* outer_joined_polyline) {
60
60
  while (act_part != nullptr) {
61
- if (all_parts.empty() || all_parts.back() != act_part) {
61
+ Part* last_part = (!all_parts.empty()) ? all_parts.back() : nullptr;
62
+ if (all_parts.empty() || last_part != act_part) {
62
63
  all_parts.push_back(act_part);
63
64
  }
64
65
 
@@ -75,6 +76,10 @@ void Cursor::traverse_outer(Part* act_part,
75
76
  outer_joined_polyline->add(position);
76
77
  }
77
78
  } else {
79
+ if (act_part->dead_end &&
80
+ all_parts.size() > 1 &&
81
+ last_part->is(Part::SEAM) &&
82
+ last_part->polyline() == act_part->polyline()) return;
78
83
  while (Position *new_position = static_cast<Position*>(act_part->iterator())) {
79
84
  if (outer_joined_polyline->size > 1 &&
80
85
  outer_joined_polyline->head->payload == new_position->payload &&
@@ -83,8 +88,9 @@ void Cursor::traverse_outer(Part* act_part,
83
88
  }
84
89
  this->cluster.positions_pool.emplace_back(this->cluster.hub(), new_position->payload);
85
90
  outer_joined_polyline->add(&this->cluster.positions_pool.back());
91
+
86
92
  for (Shape *shape : act_part->polyline()->next_tile_eligible_shapes()) {
87
- if (Part *part = shape->outer_polyline->find_first_part_by_position(new_position)) {
93
+ if (Part *part = shape->outer_polyline->find_first_part_by_position(new_position, act_part->versus())) {
88
94
  const auto n = all_parts.size();
89
95
  Part *last_last_part = n >= 2 ? all_parts[n - 2] : nullptr;
90
96
 
@@ -103,6 +109,7 @@ void Cursor::traverse_outer(Part* act_part,
103
109
  shapes_sequence.push_back(part->polyline()->shape);
104
110
  }
105
111
  part->next_position(new_position);
112
+ part->dead_end = true;
106
113
  act_part = part;
107
114
  jumped_to_new_part = true;
108
115
  break;
@@ -110,7 +117,6 @@ void Cursor::traverse_outer(Part* act_part,
110
117
  }
111
118
  }
112
119
  if (jumped_to_new_part) break;
113
- act_part->passes += 1;
114
120
  act_part->next_position(nullptr);
115
121
  }
116
122
  }
@@ -236,7 +242,7 @@ std::vector<Sequence*> Cursor::collect_inner_sequences(Sequence* outer_seq) {
236
242
  for (Part* part : all_parts)
237
243
  { part->touch();
238
244
  retme_sequence->move_from(*part, [&](QNode<Point>* pos) -> bool {
239
- Position *position = dynamic_cast<Position*>(pos);
245
+ Position *position = static_cast<Position*>(pos);
240
246
  if (part->is(Part::ADDED) &&
241
247
  !(position->payload->y >= bounds.min &&
242
248
  position->payload->y <= bounds.max)) {
@@ -277,6 +283,8 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
277
283
  for (Shape *shape : act_part->polyline()->next_tile_eligible_shapes()) {
278
284
  for (Part* dest_part : shape->outer_polyline->parts()) {
279
285
  if (dest_part->trasmuted || dest_part->is(Part::EXCLUSIVE)) continue;
286
+ int dest_part_versus = dest_part->versus();
287
+ if (dest_part_versus != 0 && dest_part_versus == act_part->versus()) continue;
280
288
  if (dest_part->intersect_part(act_part)) {
281
289
  std::vector<EndPoint*> link_seq = duplicates_intersection(*dest_part, *act_part);
282
290
  if (!link_seq.empty()) {
@@ -9,6 +9,7 @@
9
9
 
10
10
  #pragma once
11
11
  #include <vector>
12
+ #include <unordered_set>
12
13
  #include "Part.h"
13
14
 
14
15
  class Part;
@@ -19,9 +20,9 @@ class EndPoint {
19
20
 
20
21
  void set_point(Point* p) { point_ = p; }
21
22
  Point* get_point() const { return point_; }
22
- std::vector<Queueable<Point>*>& queues() { return queues_; }
23
+ std::unordered_set<Queueable<Point>*>& queues() { return queues_; }
23
24
  bool queues_include(Queueable<Point>* q) const;
24
25
  private:
25
26
  Point* point_;
26
- std::vector<Queueable<Point>*> queues_;
27
+ std::unordered_set<Queueable<Point>*> queues_;
27
28
  };
@@ -41,6 +41,7 @@ Finder::Finder(int number_of_threads, Bitmap *bitmap, Matcher *matcher, std::vec
41
41
  TilePayload p { tile_index, x, tile_end_x };
42
42
  enqueue(p, [this](const TilePayload& payload) {
43
43
  std::vector<std::string> base_arguments = {"--bounds", "--versus=" + this->options.get_alpha_versus()};
44
+ if (this->options.connectivity_offset == 1) base_arguments.push_back("--connectivity=8");
44
45
  CpuTimer t;
45
46
  t.start();
46
47
  auto* finder = new ClippedPolygonFinder(
@@ -10,6 +10,8 @@
10
10
  #include "Part.h"
11
11
  #include <iostream>
12
12
  #include <vector>
13
+ #include <string>
14
+ #include <sstream>
13
15
  #include "Polyline.h"
14
16
  #include "Tile.h"
15
17
  #include "Cluster.h"
@@ -62,7 +64,7 @@ void Part::touch()
62
64
  bool Part::intersect_part(Part* other_part)
63
65
  { bool intersect = false;
64
66
  other_part->each([&](QNode<Point>* pos) -> bool {
65
- Position *position = dynamic_cast<Position*>(pos);
67
+ Position *position = static_cast<Position*>(pos);
66
68
  if (position->end_point()->queues_include(this))
67
69
  { intersect = true;
68
70
  return(false);
@@ -76,7 +78,7 @@ std::vector<EndPoint*> Part::to_endpoints() {
76
78
  std::vector<EndPoint*> out;
77
79
  QNode<Point>* current = head;
78
80
  while (current) {
79
- out.push_back((dynamic_cast<Position*>(current))->end_point());
81
+ out.push_back((static_cast<Position*>(current))->end_point());
80
82
  current = current->next;
81
83
  }
82
84
  return out;
@@ -94,3 +96,34 @@ std::vector<EndPoint*> Part::remove_adjacent_pairs(const std::vector<EndPoint*>&
94
96
  }
95
97
  return result;
96
98
  }
99
+
100
+ void Part::orient()
101
+ { if (this->size <= 1) {
102
+ this->versus_ = 0;
103
+ } else {
104
+ this->versus_ = (this->tail->payload->y - this->head->payload->y) > 0 ? 1 : -1;
105
+ }
106
+ }
107
+
108
+ std::string Part::inspect() {
109
+ size_t part_index = 0;
110
+ auto it = std::find(this->polyline()->parts().begin(), this->polyline()->parts().end(), this);
111
+ if (it != this->polyline()->parts().end()) {
112
+ part_index = std::distance(this->polyline()->parts().begin(), it);
113
+ }
114
+ std::stringstream ss;
115
+ ss << "part " << part_index
116
+ << " (versus=" << this->versus_
117
+ << " inv=" << this->inverts
118
+ << " trm=" << this->trasmuted
119
+ << " touched=" << this->touched_
120
+ << " dead_end=" << this->dead_end
121
+ << ", " << this->size << "x) of " << this->polyline()->tile->name()
122
+ << " (" << std::to_string(static_cast<uint32_t>(type)) << ") (";
123
+ this->each([&](QNode<Point>* pos) -> bool {
124
+ ss << pos->payload->toString();
125
+ return true;
126
+ });
127
+ ss << ")";
128
+ return ss.str();
129
+ }
@@ -32,6 +32,7 @@ class Part : public Queueable<Point> {
32
32
  bool inverts = false;
33
33
  bool trasmuted = false;
34
34
  bool delayed = false;
35
+ bool dead_end = false;
35
36
  Part* next = nullptr;
36
37
  Part* prev = nullptr;
37
38
  Part* circular_next = nullptr;
@@ -39,17 +40,20 @@ class Part : public Queueable<Point> {
39
40
  Polyline* polyline() { return polyline_; }
40
41
  Position* next_position(Position* force_position);
41
42
  void add_position(Point* point);
42
- int passes = 0;
43
43
  Types type;
44
44
  bool innerable();
45
45
  const bool touched() const { return touched_; }
46
+ const int versus() const { return versus_; }
46
47
  void touch();
47
48
  bool intersect_part(Part* other_part);
48
49
  void set_polyline(Polyline* polyline) { this->polyline_ = polyline; }
49
50
  std::vector<EndPoint*> to_endpoints();
50
51
  static std::vector<EndPoint*> remove_adjacent_pairs(const std::vector<EndPoint*>& input);
52
+ void orient();
53
+ std::string inspect();
51
54
 
52
55
  private:
56
+ int versus_ = 0;
53
57
  bool touched_ = false;
54
58
  Polyline* polyline_;
55
59
  };
@@ -31,6 +31,8 @@ void Partitionable::add_part(Part* new_part)
31
31
  }
32
32
  new_part->prev = last;
33
33
  new_part->circular_next = this->parts_.front();
34
+
35
+ if (new_part->is(Part::SEAM)) new_part->orient();
34
36
  }
35
37
 
36
38
  void Partitionable::insert_after(Part* part, Part* new_part) {
@@ -46,7 +48,7 @@ void Partitionable::insert_after(Part* part, Part* new_part) {
46
48
 
47
49
  void Partitionable::partition()
48
50
  { this->parts_.clear();
49
- Polyline *polyline = dynamic_cast<Polyline*>(this);
51
+ Polyline *polyline = static_cast<Polyline*>(this);
50
52
  PartPool& pool = polyline->tile->cluster->parts_pool;
51
53
  Part *current_part = nullptr;
52
54
  int n = 0;
@@ -77,10 +79,10 @@ void Partitionable::partition()
77
79
  this->trasmute_parts();
78
80
  }
79
81
 
80
- Part* Partitionable::find_first_part_by_position(Position* position) {
82
+ Part* Partitionable::find_first_part_by_position(Position* position, int versus) {
81
83
  for (Part* part : this->parts_)
82
84
  { if ( part->is(Part::SEAM) &&
83
- part->passes == 0 &&
85
+ part->versus() == -(versus) &&
84
86
  position->end_point()->queues_include(part)) return(part);
85
87
  }
86
88
  return(nullptr);
@@ -157,7 +159,7 @@ std::optional<SewReturnData> Partitionable::sew(std::vector<std::pair<int, int>>
157
159
  }
158
160
 
159
161
  std::vector<Part*> all_parts;
160
- Polyline* polyline = dynamic_cast<Polyline*>(this);
162
+ Polyline* polyline = static_cast<Polyline*>(this);
161
163
  all_parts.reserve(before_parts.size() + after_parts.size());
162
164
  all_parts.insert(all_parts.end(), before_parts.begin(), before_parts.end());
163
165
  all_parts.insert(all_parts.end(), after_parts.begin(), after_parts.end());
@@ -194,7 +196,6 @@ std::optional<SewReturnData> Partitionable::sew(std::vector<std::pair<int, int>>
194
196
  return std::make_pair(left, right);
195
197
  }
196
198
 
197
-
198
199
  void Partitionable::trasmute_parts()
199
200
  { std::vector<Part*> insides;
200
201
  for (Part* p : parts_) {
@@ -205,16 +206,16 @@ void Partitionable::trasmute_parts()
205
206
  for (Part* inside : insides)
206
207
  { for (Part* inside_compare : insides) {
207
208
  if (inside == inside_compare || !inside_compare->is(Part::SEAM) ) continue;
208
- bool all_match = true;
209
+ int count = 0;
209
210
  inside->each([&](QNode<Point>* pos) -> bool {
210
- Position *position = dynamic_cast<Position*>(pos);
211
+ Position *position = static_cast<Position*>(pos);
211
212
  if (position->end_point()->queues_include(inside_compare))
212
- { return true;
213
+ { count++;
214
+ return true;
213
215
  }
214
- all_match = false;
215
216
  return false;
216
217
  });
217
- if (all_match) {
218
+ if (count == inside->size && count < inside_compare->size) {
218
219
  inside->type = Part::EXCLUSIVE;
219
220
  inside->trasmuted = true;
220
221
  break;
@@ -222,4 +223,3 @@ void Partitionable::trasmute_parts()
222
223
  }
223
224
  }
224
225
  }
225
-
@@ -20,8 +20,9 @@ class Partitionable {
20
20
  explicit Partitionable() {}
21
21
  virtual ~Partitionable() = default;
22
22
  void partition();
23
- Part* find_first_part_by_position(Position* position);
23
+ Part* find_first_part_by_position(Position* position, int versus);
24
24
  std::optional<SewReturnData> sew(std::vector<std::pair<int, int>> intersection, Polyline* other);
25
+ const std::vector<Part*> parts() const { return parts_; }
25
26
 
26
27
  protected:
27
28
  std::vector<Part*> parts_;
@@ -9,7 +9,9 @@
9
9
 
10
10
  #include <vector>
11
11
  #include <limits>
12
+ #include <string>
12
13
  #include <unordered_set>
14
+ #include <sstream>
13
15
  #include "Polyline.h"
14
16
  #include "Tile.h"
15
17
  #include "Shape.h"
@@ -76,7 +78,7 @@ std::vector<std::pair<int, int>> Polyline::intersection(const Polyline* other) c
76
78
  auto& part = parts_[i];
77
79
  if (!part->is(Part::SEAM) && part->trasmuted) continue;
78
80
  part->each([&](QNode<Point>* pos) -> bool {
79
- Position *position = dynamic_cast<Position*>(pos);
81
+ Position *position = static_cast<Position*>(pos);
80
82
  if (position->end_point() != nullptr)
81
83
  { this->tracked_endpoints[position->end_point()] = i;
82
84
  }
@@ -92,7 +94,7 @@ std::vector<std::pair<int, int>> Polyline::intersection(const Polyline* other) c
92
94
  continue;
93
95
  }
94
96
  other_part->each([&](QNode<Point>* pos) -> bool {
95
- Position *position = dynamic_cast<Position*>(pos);
97
+ Position *position = static_cast<Position*>(pos);
96
98
  auto it = this->tracked_endpoints.find(position->end_point());
97
99
  if (it != this->tracked_endpoints.end()) {
98
100
  int self_index = it->second;
@@ -114,3 +116,15 @@ void Polyline::clear() {
114
116
  bool Polyline::is_empty() {
115
117
  return raw_.empty();
116
118
  }
119
+
120
+ std::string Polyline::info() {
121
+ Shape* shape = this->shape;
122
+ size_t part_index = 0;
123
+ auto it = std::find(this->tile->shapes().begin(), this->tile->shapes().end(), shape);
124
+ if (it != this->tile->shapes().end()) {
125
+ part_index = std::distance(this->tile->shapes().begin(), it);
126
+ }
127
+ std::stringstream ss;
128
+ ss << "b" << this->tile->name() << " S" << part_index;
129
+ return ss.str();
130
+ }
@@ -14,6 +14,7 @@
14
14
  #include <utility>
15
15
  #include <vector>
16
16
  #include <unordered_map>
17
+ #include <string>
17
18
  #include "Partitionable.h"
18
19
  #include "../RectBounds.h"
19
20
 
@@ -47,6 +48,7 @@ class Polyline : public Partitionable {
47
48
  bool vert_intersect(Polyline& other);
48
49
  void reset_tracked_endpoints();
49
50
  bool mixed_tile_origin = false;
51
+ std::string info();
50
52
 
51
53
  private:
52
54
  std::vector<Point*> raw_;
@@ -30,12 +30,9 @@ Position::Position(EndPoint* end_point)
30
30
  }
31
31
 
32
32
  void Position::before_rem(Queueable<Point>* q) {
33
- if (this->end_point_ != nullptr) {
34
- auto& queues = this->end_point_->queues();
35
- queues.erase(std::remove(queues.begin(), queues.end(), q), queues.end());
36
- }
33
+ if (this->end_point_ != nullptr) this->end_point_->queues().erase(q);
37
34
  }
38
35
 
39
36
  void Position::after_add(Queueable<Point>* q) {
40
- if (this->end_point_ != nullptr) this->end_point_->queues().push_back(q);
37
+ if (this->end_point_ != nullptr) this->end_point_->queues().insert(q);
41
38
  }
@@ -1,9 +1,19 @@
1
1
  require "mkmf-rice"
2
2
 
3
+ has_tcmalloc = find_library("tcmalloc", "malloc")
4
+
3
5
  # rubocop:disable Style/GlobalVars
4
6
 
5
- $CXXFLAGS << " -std=c++17 -pthread -march=native -DNDEBUG -Ofast -flto -g -fno-omit-frame-pointer"
6
- $CFLAGS << " -std=c99 -pthread -march=native -DNDEBUG -Ofast -flto -g -fno-omit-frame-pointer"
7
+ $CXXFLAGS << " -std=c++17 -pthread -march=native -DNDEBUG -Ofast -flto"
8
+ $CFLAGS << " -std=c11 -pthread -march=native -DNDEBUG -Ofast -flto"
9
+
10
+ if has_tcmalloc
11
+ $LDFLAGS << " -Wl,--no-as-needed -ltcmalloc"
12
+ puts "tcmalloc linked to gem."
13
+ else
14
+ puts "tcmalloc not found; standard malloc will be used."
15
+ end
16
+
7
17
  $LDFLAGS << " -lz -lstdc++ -flto -pthread"
8
18
 
9
19
  $objs = [
@@ -28,6 +28,7 @@ module Contrek
28
28
  png_image.draw_line(poly[:outer][0][:x], poly[:outer][0][:y], poly[:outer][-1][:x], poly[:outer][-1][:y], color)
29
29
  color = ChunkyPNG::Color("green @ 1.0")
30
30
  poly[:inner].each do |sequence|
31
+ next if sequence.empty?
31
32
  sequence.each_cons(2) do |coords|
32
33
  png_image.draw_line(coords[0][:x], coords[0][:y], coords[1][:x], coords[1][:y], color)
33
34
  end