contrek 1.0.7 → 1.0.9

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/CHANGELOG.md +11 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +80 -8
  5. data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +39 -0
  6. data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +43 -0
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +72 -0
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +19 -24
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.h +1 -2
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +3 -5
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +1 -1
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +2 -2
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +1 -1
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +29 -9
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +1 -0
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +11 -1
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.cpp +6 -5
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.h +12 -8
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +2 -1
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +2 -0
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +108 -66
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +5 -3
  23. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +35 -28
  24. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +5 -1
  25. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +13 -10
  26. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.h +1 -1
  27. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +16 -22
  28. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +1 -1
  29. data/lib/contrek/bitmaps/rgb_color.rb +0 -15
  30. data/lib/contrek/bitmaps/rgb_cpp_color.rb +10 -0
  31. data/lib/contrek/finder/concurrent/cluster.rb +2 -2
  32. data/lib/contrek/finder/concurrent/cursor.rb +34 -16
  33. data/lib/contrek/finder/concurrent/finder.rb +12 -1
  34. data/lib/contrek/finder/concurrent/hub.rb +3 -4
  35. data/lib/contrek/finder/concurrent/part.rb +8 -2
  36. data/lib/contrek/finder/concurrent/partitionable.rb +61 -33
  37. data/lib/contrek/finder/concurrent/polyline.rb +45 -2
  38. data/lib/contrek/finder/concurrent/position.rb +3 -4
  39. data/lib/contrek/finder/concurrent/queueable.rb +12 -20
  40. data/lib/contrek/version.rb +1 -1
  41. data/lib/contrek.rb +4 -3
  42. metadata +6 -4
  43. data/ext/cpp_polygon_finder/PolygonFinder/Makefile +0 -44
  44. data/ext/cpp_polygon_finder/PolygonFinder/src/Main.cpp +0 -50
@@ -8,7 +8,7 @@ module Contrek
8
8
  ADDED = 2
9
9
 
10
10
  attr_reader :polyline, :index, :touched
11
- attr_accessor :next, :circular_next, :prev, :type, :passes, :inverts, :trasmuted
11
+ attr_accessor :next, :circular_next, :prev, :type, :passes, :inverts, :trasmuted, :delayed
12
12
  def initialize(type, polyline)
13
13
  @type = type
14
14
  @polyline = polyline
@@ -19,14 +19,20 @@ module Contrek
19
19
  @touched = false
20
20
  @inverts = false
21
21
  @trasmuted = false
22
+ @delayed = false
22
23
  end
23
24
 
24
25
  def is?(type)
25
26
  @type == type
26
27
  end
27
28
 
29
+ def set_polyline(polyline)
30
+ @polyline = polyline
31
+ end
32
+
28
33
  def add_position(position)
29
- add(Position.new(position: position, hub: polyline.tile.cluster.hub))
34
+ hub = is?(EXCLUSIVE) ? nil : polyline.tile.cluster.hub
35
+ add(Position.new(position: position, hub: hub))
30
36
  end
31
37
 
32
38
  def next_position(force_position = nil)
@@ -16,6 +16,15 @@ module Contrek
16
16
  new_part.prev = last
17
17
  end
18
18
 
19
+ def insert_after(part, new_part)
20
+ part_index = @parts.index(part)
21
+ @parts.insert(part_index + 1, new_part)
22
+ new_part.prev = part
23
+ new_part.next = new_part.circular_next = part.next
24
+ part.next.prev = new_part if part.next
25
+ part.next = part.circular_next = new_part
26
+ end
27
+
19
28
  def find_first_part_by_position(position)
20
29
  @parts.find do |part|
21
30
  part.is?(Part::SEAM) &&
@@ -57,48 +66,67 @@ module Contrek
57
66
  end
58
67
 
59
68
  def sew!(intersection, other)
60
- matching_part_indexes = []
61
- parts.each_with_index do |part, index|
62
- next if part.trasmuted
63
- matching_part_indexes << index if part.intersection_with_array?(intersection)
64
- end
65
- other_matching_part_indexes = []
66
- other.parts.each_with_index do |part, index|
67
- next if part.trasmuted
68
- other_matching_part_indexes << index if part.intersection_with_array?(intersection)
69
- end
70
- # other_matching_part_indexes and matching_part_indexes always contain at least one element
69
+ matching_part_indexes, other_matching_part_indexes = intersection.transpose.map(&:sort)
70
+ # other_matching_part_indexes and matching_part_indexes always must contain at least one element
71
71
  before_parts = other.parts[other_matching_part_indexes.last + 1..]
72
72
  after_parts = other_matching_part_indexes.first.zero? ? [] : other.parts[0..other_matching_part_indexes.first - 1]
73
73
  part_start = parts[matching_part_indexes.first]
74
74
  part_end = parts[matching_part_indexes.last]
75
75
 
76
- # They are inverted since they traverse in opposite directions
77
- sequence = Sequence.new
78
- sequence.add part_start.head
79
- before_parts.each { |part| sequence.append(part) }
80
- after_parts.each { |part| sequence.append(part) }
81
- sequence.add part_end.tail if part_end.tail # nil when part_start == part_end
82
-
83
- part_start.replace!(sequence)
84
- part_start.type = Part::EXCLUSIVE
85
- part_end.reset! if part_start != part_end
76
+ # left and right side reduces will be combined and later converted into orphan inners sequences
77
+ returning_data = [[matching_part_indexes, parts], [other_matching_part_indexes, other.parts]].map do |matching_part_indexes, parts|
78
+ lastn = 0
79
+ result = []
80
+ (matching_part_indexes.first + 1).upto(matching_part_indexes.last - 1) do |n|
81
+ if matching_part_indexes.index(n).nil?
82
+ part = parts[n]
83
+ if part.is?(Part::SEAM) && part.size > 0 && !part.delayed # fallback, delays the shape
84
+ part.delayed = true
85
+ return nil
86
+ end
87
+ if (lastn == (n - 1)) && result.any?
88
+ result.last.concat part.to_a
89
+ else
90
+ result << part.to_a
91
+ end
92
+ lastn = n
93
+ end
94
+ end
95
+ result
96
+ end
86
97
 
87
- left = []
88
- (matching_part_indexes.first + 1).upto(matching_part_indexes.last - 1) do |n|
89
- left << parts[n].to_a if matching_part_indexes.index(n).nil?
98
+ if part_start != part_end
99
+ (matching_part_indexes.last - 1).downto(matching_part_indexes.first + 1) do |n|
100
+ delete_part = parts[n]
101
+ delete_part.prev.next = delete_part.next if delete_part.prev
102
+ delete_part.next.prev = delete_part.prev if delete_part.next
103
+ parts.delete_at(n)
104
+ end
90
105
  end
91
- (matching_part_indexes.last - 1).downto(matching_part_indexes.first + 1) do |n|
92
- delete_part = parts[n]
93
- delete_part.prev.next = delete_part.next if delete_part.prev
94
- delete_part.next.prev = delete_part.prev if delete_part.next
95
- parts.delete_at(n)
106
+
107
+ all_parts = before_parts + after_parts
108
+ will_be_last = all_parts.last
109
+ all_parts.reverse_each do |part|
110
+ insert_after(part_start, part)
111
+ other.parts.delete(part)
112
+ part.set_polyline(self)
96
113
  end
97
- right = []
98
- (other_matching_part_indexes.first + 1).upto(other_matching_part_indexes.last - 1) do |n|
99
- right << other.parts[n].to_a if other_matching_part_indexes.index(n).nil?
114
+
115
+ part_start.type = Part::EXCLUSIVE
116
+ new_end_part = Part.new(Part::EXCLUSIVE, self)
117
+ new_end_part.add(part_end.tail)
118
+ part_start.singleton! # reduce part to its head only
119
+
120
+ if part_start != part_end
121
+ part_end.prev.next = part_end.next if part_end.prev
122
+ part_end.next.prev = part_end.prev if part_end.next
123
+ parts.delete(part_end)
100
124
  end
101
- [left, right]
125
+ insert_after(will_be_last, new_end_part)
126
+
127
+ reset_tracked_endpoints!
128
+
129
+ returning_data
102
130
  end
103
131
 
104
132
  private
@@ -27,7 +27,15 @@ module Contrek
27
27
  end
28
28
 
29
29
  def inspect
30
- "#{self.class}[b#{@tile.name} S#{@name} #{"B" if boundary?}] (#{raw.count} => #{raw.inspect})"
30
+ "#{self.class}#{named} (#{raw.count} => #{raw.inspect})"
31
+ end
32
+
33
+ def named
34
+ "[b#{@tile.name} S#{@name} #{"B" if boundary?}]"
35
+ end
36
+
37
+ def numpy_raw
38
+ raw.flat_map { |p| [p[:x], p[:y]] }
31
39
  end
32
40
 
33
41
  def info
@@ -46,8 +54,36 @@ module Contrek
46
54
  (@flags & flag) != 0
47
55
  end
48
56
 
57
+ def reset_tracked_endpoints!
58
+ @tracked_endpoints = nil
59
+ end
60
+
61
+ # returns for every position of intersection an array composed by the indexes of parts (self,other) involved
62
+ # es [[1,3],[2,6],...]. The first time the sequence for self is computed is stored.
49
63
  def intersection(other)
50
- @raw & other.raw
64
+ if @tracked_endpoints.nil?
65
+ @tracked_endpoints = {} # memoize found sequence
66
+ parts.each_with_index do |part, part_index|
67
+ next if !part.is?(Part::SEAM) && part.trasmuted
68
+ part.each do |pos|
69
+ next if pos.end_point.nil?
70
+ @tracked_endpoints[pos.end_point.object_id] = part_index
71
+ end
72
+ end
73
+ end
74
+ matching_parts = []
75
+ other.parts.each_with_index do |part, part_index|
76
+ next if !part.is?(Part::SEAM) && part.trasmuted
77
+ part.each do |pos|
78
+ if (self_index = @tracked_endpoints[pos.end_point.object_id])
79
+ matching_parts << [self_index, part_index]
80
+ false
81
+ else
82
+ true
83
+ end
84
+ end
85
+ end
86
+ matching_parts
51
87
  end
52
88
 
53
89
  def empty?
@@ -82,6 +118,13 @@ module Contrek
82
118
  !(@max_y < other.min_y || other.max_y < @min_y)
83
119
  end
84
120
 
121
+ def get_bounds
122
+ {min_x: @min_x,
123
+ max_x: @max_x,
124
+ min_y: @min_y,
125
+ max_y: @max_y}
126
+ end
127
+
85
128
  private
86
129
 
87
130
  def find_boundary
@@ -6,8 +6,7 @@ module Contrek
6
6
  attr_reader :end_point
7
7
 
8
8
  def initialize(hub:, position:)
9
- key = position[:y] * hub.width + position[:x]
10
- @end_point = hub.payloads[key] ||= EndPoint.new
9
+ @end_point = hub.payloads[position[:y]] ||= EndPoint.new if hub
11
10
  @position = position
12
11
  end
13
12
 
@@ -16,11 +15,11 @@ module Contrek
16
15
  end
17
16
 
18
17
  def after_add(new_queue)
19
- @end_point.queues << new_queue
18
+ @end_point.queues << new_queue if @end_point
20
19
  end
21
20
 
22
21
  def before_rem(old_queue)
23
- @end_point.queues.delete(old_queue)
22
+ @end_point&.queues&.delete(old_queue)
24
23
  end
25
24
  end
26
25
  end
@@ -10,6 +10,16 @@ module Contrek
10
10
  @size = 0
11
11
  end
12
12
 
13
+ def singleton!
14
+ if @head&.next
15
+ @head.next.prev = nil
16
+ @head.next = nil
17
+ end
18
+ @tail = nil
19
+ @size = 1
20
+ @iterator = 0
21
+ end
22
+
13
23
  def rem(node)
14
24
  Raise "Not my node" if node.owner != self
15
25
 
@@ -84,12 +94,13 @@ module Contrek
84
94
  @iterator = 0
85
95
  end
86
96
 
97
+ # from yield: false => stop, true => continue
87
98
  def each(&block)
88
99
  last = nil
89
100
  unless @head.nil?
90
101
  pointer = @head
91
102
  loop do
92
- yield(pointer)
103
+ break unless yield(pointer)
93
104
  last = pointer
94
105
  break unless (pointer = pointer.next)
95
106
  end
@@ -142,25 +153,6 @@ module Contrek
142
153
  rem(@tail)
143
154
  end
144
155
 
145
- def intersection_with(queueable)
146
- int = []
147
- each do |node|
148
- int += queueable.map do |e|
149
- break [e.payload] if e.payload == node.payload
150
- end.compact
151
- end
152
- int
153
- end
154
-
155
- def intersection_with_array?(array)
156
- each { |node| return true if array.index(node.payload) }
157
- false
158
- end
159
-
160
- def intersect_with?(queueable)
161
- intersection_with(queueable).any?
162
- end
163
-
164
156
  def remove_adjacent_pairs(array = nil)
165
157
  array = to_a if array.nil?
166
158
  n = array.size
@@ -1,3 +1,3 @@
1
1
  module Contrek
2
- VERSION = "1.0.7"
2
+ VERSION = "1.0.9"
3
3
  end
data/lib/contrek.rb CHANGED
@@ -5,6 +5,7 @@ require "contrek/bitmaps/chunky_bitmap"
5
5
  require "contrek/bitmaps/png_bitmap"
6
6
  require "contrek/bitmaps/custom_bitmap"
7
7
  require "contrek/bitmaps/rgb_color"
8
+ require "contrek/bitmaps/rgb_cpp_color"
8
9
  require "contrek/finder/bounds"
9
10
  require "contrek/bitmaps/sample_generator"
10
11
  require "contrek/finder/list"
@@ -54,10 +55,10 @@ module Contrek
54
55
  private
55
56
 
56
57
  def compute_cpp(png_file_path, options)
57
- color = Bitmaps::RgbColor.new(**options[:color])
58
+ color = Bitmaps::RgbCppColor.new(**options[:color])
58
59
  png_bitmap = CPPPngBitMap.new(png_file_path)
59
60
  rgb_matcher_klass = (options[:class] == "value_not_matcher") ? CPPRGBNotMatcher : CPPRGBMatcher
60
- rgb_matcher = rgb_matcher_klass.new(color.to_rgb_raw)
61
+ rgb_matcher = rgb_matcher_klass.new(color.raw)
61
62
  if options.key?(:number_of_threads) || options[:finder]&.key?(:number_of_tiles)
62
63
  Contrek::Cpp::CPPConcurrentFinder.new(
63
64
  number_of_threads: options.dig(:number_of_threads) || 0,
@@ -74,7 +75,7 @@ module Contrek
74
75
  end
75
76
 
76
77
  def compute_ruby_pure(png_file_path, options)
77
- color = Bitmaps::RgbColor.new(**options[:color])
78
+ color = Bitmaps::RgbCppColor.new(**options[:color])
78
79
  png_bitmap = Bitmaps::PngBitmap.new(png_file_path)
79
80
  rgb_matcher = const_get("Contrek::Matchers::" + camelize("value_not_matcher")).new(color.raw)
80
81
  if options.key?(:number_of_threads) || options[:finder]&.key?(:number_of_tiles)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contrek
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emanuele Cesaroni
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-10 00:00:00.000000000 Z
11
+ date: 2026-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -140,10 +140,11 @@ files:
140
140
  - Rakefile
141
141
  - contrek.gemspec
142
142
  - contrek.png
143
- - ext/cpp_polygon_finder/PolygonFinder/Makefile
143
+ - ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt
144
+ - ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp
144
145
  - ext/cpp_polygon_finder/PolygonFinder/images/labyrinth.png
145
146
  - ext/cpp_polygon_finder/PolygonFinder/images/sample_10240x10240.png
146
- - ext/cpp_polygon_finder/PolygonFinder/src/Main.cpp
147
+ - ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h
147
148
  - ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp
148
149
  - ext/cpp_polygon_finder/PolygonFinder/src/Tests.h
149
150
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/CpuTimer.h
@@ -229,6 +230,7 @@ files:
229
230
  - lib/contrek/bitmaps/painting.rb
230
231
  - lib/contrek/bitmaps/png_bitmap.rb
231
232
  - lib/contrek/bitmaps/rgb_color.rb
233
+ - lib/contrek/bitmaps/rgb_cpp_color.rb
232
234
  - lib/contrek/bitmaps/sample_generator.rb
233
235
  - lib/contrek/cpp/cpp_concurrent_finder.rb
234
236
  - lib/contrek/finder/bounds.rb
@@ -1,44 +0,0 @@
1
- CXX = g++
2
- CXXFLAGS = -Wall -Wextra -std=c++17 -Isrc -MMD -MP -pthread -g \
3
- -fno-omit-frame-pointer -fsanitize=address,undefined \
4
- -O0
5
- LDFLAGS = -lpng -pthread -lz -fsanitize=address,undefined
6
-
7
- # valgrinf or perf setup
8
- #CXXFLAGS = -Wall -Wextra -std=c++17 -Isrc -MMD -MP -pthread -g -fno-omit-frame-pointer -march=native -DNDEBUG -O3
9
- #LDFLAGS = -lpng -pthread -lz
10
-
11
- # best performances
12
- CXXFLAGS = -Wall -Wextra -std=c++17 -Isrc -MMD -MP -pthread -g -march=native -DNDEBUG -Ofast -flto
13
- LDFLAGS = -lpng -pthread -lz -flto=auto
14
-
15
- SRC = $(shell find src -name '*.cpp')
16
- C_SRC = $(shell find src -name 'spng.c')
17
-
18
- OBJ = $(patsubst src/%.cpp, build/%.o, $(SRC))
19
- OBJ += $(patsubst src/%.c, build/%.o, $(C_SRC))
20
-
21
- DEPS = $(OBJ:.o=.d)
22
-
23
- EXEC = contrek
24
-
25
- all: $(EXEC)
26
-
27
- $(EXEC): $(OBJ)
28
- $(CXX) $(CXXFLAGS) $(OBJ) -o $@ -Wl,--no-as-needed $(LDFLAGS)
29
-
30
- build/%.o: src/%.cpp
31
- @mkdir -p $(dir $@)
32
- $(CXX) $(CXXFLAGS) -c $< -o $@
33
-
34
- build/%.o: src/%.c
35
- @mkdir -p $(dir $@)
36
- $(CC) -O3 -march=native -fPIC -c $< -o $@
37
-
38
- -include $(DEPS)
39
-
40
- clean:
41
- rm -rf build $(EXEC)
42
-
43
- run: all
44
- ./$(EXEC)
@@ -1,50 +0,0 @@
1
- //============================================================================
2
- // Name : PolygonFinder.cpp
3
- // Author : Emanuele Cesaroni
4
- // Version :
5
- // Copyright : 2025 Emanuele Cesaroni
6
- // Description :
7
- //============================================================================
8
-
9
- #include <string.h>
10
- #include <iostream>
11
- #include <list>
12
- #include <map>
13
- #include <string>
14
- #include "polygon/finder/PolygonFinder.h"
15
- #include "polygon/bitmaps/Bitmap.h"
16
- #include "polygon/bitmaps/FastPngBitmap.h"
17
- #include "polygon/bitmaps/RemoteFastPngBitmap.h"
18
- #include "polygon/matchers/Matcher.h"
19
- #include "polygon/matchers/RGBMatcher.h"
20
- #include "polygon/matchers/RGBNotMatcher.h"
21
- #include "polygon/matchers/ValueNotMatcher.h"
22
- #include "polygon/finder/optionparser.h"
23
- #include "Tests.h"
24
- #include "polygon/CpuTimer.h"
25
-
26
- int main() {
27
- CpuTimer cpu_timer;
28
- Tests test_suite;
29
- cpu_timer.start();
30
-
31
- // test_suite.test_a();
32
- // test_suite.test_b();
33
- // test_suite.test_c();
34
- // test_suite.test_d();
35
- test_suite.test_e();
36
- std::cout << "compute time =" << cpu_timer.stop() << std::endl;
37
-
38
- /*
39
- FastPngBitmap png_bitmap("images/labyrinth.png");
40
- std::cout << "image_w=" << png_bitmap.w() << " image_h=" << png_bitmap.h() << std::endl;
41
- std::cout << "load_error=" << png_bitmap.error() << std::endl;
42
-
43
- std::string data_url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==";
44
- data_url.erase(0, 22);
45
- RemoteFastPngBitmap bitmap(&data_url);
46
- std::cout << "image_w=" << bitmap.w() << " image_h=" << bitmap.h() << std::endl;
47
- std::cout << "load_error=" << bitmap.error() << std::endl;
48
- */
49
- return 0;
50
- }