contrek 1.0.0

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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +84 -0
  6. data/LICENSE.md +9 -0
  7. data/README.md +118 -0
  8. data/Rakefile +19 -0
  9. data/contrek.gemspec +23 -0
  10. data/contrek.png +0 -0
  11. data/ext/cpp_polygon_finder/PolygonFinder/.cproject +136 -0
  12. data/ext/cpp_polygon_finder/PolygonFinder/.project +27 -0
  13. data/ext/cpp_polygon_finder/PolygonFinder/.settings/org.eclipse.ltk.core.refactoring.prefs +2 -0
  14. data/ext/cpp_polygon_finder/PolygonFinder/images/labyrinth.png +0 -0
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/Main.cpp +41 -0
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +69 -0
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.h +19 -0
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/Bitmap.cpp +52 -0
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/Bitmap.h +32 -0
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +656 -0
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.h +42 -0
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/PngBitmap.cpp +48 -0
  23. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/PngBitmap.h +32 -0
  24. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RemoteFastPngBitmap.cpp +30 -0
  25. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RemoteFastPngBitmap.h +26 -0
  26. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/X_picopng.cpp +576 -0
  27. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/List.cpp +120 -0
  28. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/List.h +40 -0
  29. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Lists.cpp +36 -0
  30. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Lists.h +30 -0
  31. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +111 -0
  32. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +80 -0
  33. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +325 -0
  34. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.h +59 -0
  35. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +206 -0
  36. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +69 -0
  37. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/optionparser.h +2858 -0
  38. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/matchers/Matcher.cpp +23 -0
  39. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/matchers/Matcher.h +23 -0
  40. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/matchers/RGBMatcher.cpp +20 -0
  41. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/matchers/RGBMatcher.h +23 -0
  42. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/matchers/RGBNotMatcher.cpp +20 -0
  43. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/matchers/RGBNotMatcher.h +23 -0
  44. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/matchers/ValueNotMatcher.cpp +20 -0
  45. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/matchers/ValueNotMatcher.h +21 -0
  46. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/LinearReducer.cpp +40 -0
  47. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/LinearReducer.h +23 -0
  48. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/Reducer.cpp +19 -0
  49. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/Reducer.h +25 -0
  50. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/UniqReducer.cpp +30 -0
  51. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/UniqReducer.h +21 -0
  52. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/VisvalingamReducer.cpp +50 -0
  53. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/VisvalingamReducer.h +121 -0
  54. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +260 -0
  55. data/ext/cpp_polygon_finder/extconf.rb +2 -0
  56. data/lib/contrek/bitmaps/bitmap.rb +21 -0
  57. data/lib/contrek/bitmaps/chunky_bitmap.rb +33 -0
  58. data/lib/contrek/bitmaps/painting.rb +40 -0
  59. data/lib/contrek/bitmaps/png_bitmap.rb +62 -0
  60. data/lib/contrek/bitmaps/rgb_color.rb +25 -0
  61. data/lib/contrek/finder/list.rb +132 -0
  62. data/lib/contrek/finder/list_entry.rb +11 -0
  63. data/lib/contrek/finder/listable.rb +8 -0
  64. data/lib/contrek/finder/lists.rb +25 -0
  65. data/lib/contrek/finder/node.rb +126 -0
  66. data/lib/contrek/finder/node_cluster.rb +294 -0
  67. data/lib/contrek/finder/polygon_finder.rb +121 -0
  68. data/lib/contrek/map/mercator_projection.rb +76 -0
  69. data/lib/contrek/matchers/matcher.rb +20 -0
  70. data/lib/contrek/matchers/matcher_hsb.rb +24 -0
  71. data/lib/contrek/matchers/value_not_matcher.rb +9 -0
  72. data/lib/contrek/reducers/linear_reducer.rb +25 -0
  73. data/lib/contrek/reducers/reducer.rb +14 -0
  74. data/lib/contrek/reducers/uniq_reducer.rb +9 -0
  75. data/lib/contrek/reducers/visvalingam_reducer.rb +139 -0
  76. data/lib/contrek/version.rb +3 -0
  77. data/lib/contrek.rb +58 -0
  78. metadata +175 -0
@@ -0,0 +1,126 @@
1
+ module Contrek
2
+ module Finder
3
+ class Node
4
+ include Listable
5
+ attr_reader :min_x, :max_x, :y, :name, :tangs, :tangs_sequence, :tangs_count, :data_pointer
6
+ attr_accessor :track, :abs_x_index, :outer_index, :inner_index
7
+
8
+ T_UP = -1
9
+ T_DOWN = 1
10
+ OMAX = 1 << 0
11
+ OMIN = 1 << 1
12
+ IMAX = 1 << 2
13
+ IMIN = 1 << 3
14
+ OCOMPLETE = OMIN | OMAX
15
+
16
+ TURN_MAX = IMAX | OMAX
17
+ TURN_MIN = IMIN | OMIN
18
+
19
+ # 0 = outer
20
+ # 1 = inner
21
+ TURNER = [[OMAX, OMIN], [TURN_MAX, TURN_MIN]]
22
+
23
+ OUTER = 0
24
+ INNER = 1
25
+
26
+ def initialize(cluster, min_x, max_x, y, name)
27
+ @name = name
28
+ @min_x = min_x
29
+ @max_x = max_x
30
+ @y = y
31
+ @tangs = {T_UP => [], T_DOWN => []}
32
+ @tangs_sequence = nil
33
+ @tangs_count = 0
34
+ @track = 0
35
+ @abs_x_index = 0
36
+ @data_pointer = cluster.lists.get_data_pointer
37
+ @up_indexer = 0
38
+ @down_indexer = 0
39
+ @outer_index = -1
40
+ @inner_index = -1
41
+ cluster.add_node(self)
42
+ end
43
+
44
+ def tangs?(node)
45
+ @min_x <= node.max_x && node.min_x <= @max_x
46
+ end
47
+
48
+ def my_next(last, versus, mode)
49
+ last_node_index = if last.y < y
50
+ last.abs_x_index + @up_indexer
51
+ else
52
+ @down_indexer - last.abs_x_index
53
+ end
54
+
55
+ case mode
56
+ when :outer
57
+ if versus == :o
58
+ (last_node_index == tangs_sequence.size - 1) ? last_node_index = 0 : last_node_index += 1
59
+ else
60
+ (last_node_index == 0) ? last_node_index = tangs_sequence.size - 1 : last_node_index -= 1
61
+ end
62
+
63
+ when :inner
64
+
65
+ if versus == :o
66
+ (last_node_index == 0) ? last_node_index = tangs_sequence.size - 1 : last_node_index -= 1
67
+ else
68
+ (last_node_index == tangs_sequence.size - 1) ? last_node_index = 0 : last_node_index += 1
69
+ end
70
+
71
+ end
72
+ tangs_sequence.at(last_node_index)
73
+ end
74
+
75
+ def coords_entering_to(enter_to, enter_mode, tracking)
76
+ enter_to_index = if enter_to.y < y
77
+ enter_to.abs_x_index + @up_indexer
78
+ else
79
+ @down_indexer - enter_to.abs_x_index
80
+ end
81
+ ds = tangs_sequence[enter_to_index]
82
+ coords_source = ds.send(enter_mode)
83
+ enter_to.track |= TURNER[tracking][coords_source[:m] - 1]
84
+ coords_source[:point]
85
+ end
86
+
87
+ def tangs_with_x?(x)
88
+ x.between?(@min_x, @max_x)
89
+ end
90
+
91
+ def track_uncomplete
92
+ (@track & OCOMPLETE) != OCOMPLETE
93
+ end
94
+
95
+ def track_complete
96
+ (@track & OCOMPLETE) == OCOMPLETE
97
+ end
98
+
99
+ def add_intersection(other_node)
100
+ if other_node.y < y
101
+ @tangs[T_UP] << other_node
102
+ else
103
+ @tangs[T_DOWN] << other_node
104
+ end
105
+ end
106
+
107
+ def precalc_tangs_sequences
108
+ @tangs_sequence = Array.new(tangs[T_UP].size + tangs[T_DOWN].size)
109
+ tangs = self.tangs[T_UP].sort_by(&:min_x)
110
+ n = -1
111
+ @up_indexer = -tangs[0].abs_x_index if tangs.size > 0
112
+ tangs.each do |t_node|
113
+ nd = Contrek::Finder::PolygonFinder::NodeDescriptor.new(t_node, {point: {x: t_node.max_x, y: t_node.y}, m: OMAX}, {point: {x: t_node.min_x, y: t_node.y}, m: OMIN})
114
+ tangs_sequence[n += 1] = nd
115
+ end
116
+ tangs = self.tangs[T_DOWN].sort_by(&:min_x).reverse
117
+ @down_indexer = (tangs.last.abs_x_index + self.tangs[T_DOWN].size + self.tangs[T_UP].size - 1) if tangs.size > 0
118
+ tangs.each do |t_node|
119
+ nd = Contrek::Finder::PolygonFinder::NodeDescriptor.new(t_node, {point: {x: t_node.min_x, y: t_node.y}, m: OMIN}, {point: {x: t_node.max_x, y: t_node.y}, m: OMAX})
120
+ tangs_sequence[n += 1] = nd
121
+ end
122
+ @tangs_count = tangs_sequence.size
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,294 @@
1
+ module Contrek
2
+ module Finder
3
+ class NodeCluster
4
+ attr_reader :root_nodes, :sequences, :polygons, :lists, :treemap, :vert_nodes
5
+ VERSUS_INVERTER = {a: :o, o: :a}
6
+
7
+ def initialize(h, options)
8
+ @options = options
9
+ @vert_nodes = Array.new(h) { [] } # per y immetto i nodi
10
+ @sequences = []
11
+ @polygons = []
12
+ @treemap = []
13
+ @nodes = 0
14
+ @lists = Contrek::Finder::Lists.new
15
+ @root_nodes = @lists.add_list
16
+ @inner_plot = @lists.add_list
17
+ @inner_new = @lists.add_list
18
+ end
19
+
20
+ def path_sequences
21
+ @polygons.compact.each do |polygon|
22
+ yield polygon[:outer]
23
+ polygon[:inner].each do |sequence|
24
+ yield sequence
25
+ end
26
+ end
27
+ end
28
+
29
+ def compress_coords
30
+ path_sequences do |seq|
31
+ Contrek::Reducers::UniqReducer.new(points: seq).reduce! if @options[:compress].has_key?(:uniq)
32
+ Contrek::Reducers::LinearReducer.new(points: seq, options: @options[:compress][:linear]).reduce! if @options[:compress].has_key?(:linear)
33
+ Contrek::Reducers::VisvalingamReducer.new(points: seq, options: @options[:compress][:visvalingam]).reduce! if @options[:compress].has_key?(:visvalingam)
34
+ end
35
+ end
36
+
37
+ # nominal sequence
38
+ def named_sequence
39
+ @plot_sequence.map(&:name).join
40
+ end
41
+
42
+ # builds node sequences (with space coordinates) array scanning upper and lower element
43
+ # needs root_nodes ready
44
+ def build_tangs_sequence
45
+ @vert_nodes.each do |line|
46
+ line.each do |node|
47
+ node.precalc_tangs_sequences
48
+ end
49
+ end
50
+ end
51
+
52
+ def plot(bitmap)
53
+ versus = @options[:versus]
54
+ inner_v = VERSUS_INVERTER[versus]
55
+ index_order = 0
56
+ while @root_nodes.size > 0
57
+ root_node = @root_nodes.shift
58
+ root_node.outer_index = index_order
59
+ @plot_sequence = []
60
+ @sequence_coords = []
61
+ # external polygon
62
+ @plot_sequence << root_node
63
+ next_node_nd = if versus == :a
64
+ root_node.tangs_sequence.last
65
+ else
66
+ root_node.tangs_sequence.first
67
+ end
68
+ #---------------
69
+ if !next_node_nd.nil?
70
+ next_node = next_node_nd.node
71
+ @sequence_coords << next_node.coords_entering_to(root_node, VERSUS_INVERTER[versus], Contrek::Finder::Node::OUTER)
72
+ end
73
+ #---------------
74
+ plot_node(next_node, root_node, versus) if @nodes > 0 && !next_node_nd.nil?
75
+
76
+ draw_sequence(bitmap, "X") unless bitmap.nil?
77
+ @polygons << {outer: @sequence_coords, inner: []} if @sequence_coords.size >= 2
78
+ @sequences << @plot_sequence
79
+
80
+ @count = 0
81
+ index_inner = 0
82
+ while @inner_plot.size > 0
83
+ @plot_sequence = []
84
+ @sequence_coords = []
85
+ # mia test
86
+ first = @inner_plot.find { |x| x.tangs_count <= 2 } || @inner_plot.first
87
+
88
+ @plot_sequence << first
89
+ @inner_plot.delete(first)
90
+ @root_nodes.delete(first)
91
+
92
+ first.inner_index = index_inner
93
+
94
+ # @count += 1
95
+ # if @count > 10000
96
+ # puts "Houston, we have a problem!"
97
+ # break
98
+ # end
99
+
100
+ next_node = if (first.track & Contrek::Finder::Node::OMAX) != 0
101
+ (inner_v == :a) ? first.tangs[Contrek::Finder::Node::T_UP].first : first.tangs[Contrek::Finder::Node::T_DOWN].first
102
+ else
103
+ (inner_v == :a) ? first.tangs[Contrek::Finder::Node::T_DOWN].last : first.tangs[Contrek::Finder::Node::T_UP].last
104
+ end
105
+
106
+ if !next_node.nil?
107
+ @sequence_coords << next_node.coords_entering_to(first, inner_v, Contrek::Finder::Node::INNER)
108
+ end
109
+
110
+ plot_inner_node(next_node, inner_v, first, root_node) if !next_node.nil?
111
+
112
+ draw_sequence(bitmap, "+") unless bitmap.nil?
113
+
114
+ @polygons.last[:inner] << @sequence_coords
115
+
116
+ @inner_plot.grab(@inner_new)
117
+ index_inner += 1
118
+ end
119
+ # tree
120
+ @treemap << ((versus == :a) ? test_in_hole_a(root_node) : test_in_hole_o(root_node)) if @options.has_key?(:treemap)
121
+ index_order += 1
122
+ end
123
+ end
124
+
125
+ def test_in_hole_a(node)
126
+ if node.outer_index > 0
127
+ start_left = node.abs_x_index - 1
128
+ loop do
129
+ prev = @vert_nodes[node.y][start_left]
130
+ if ((cindex = prev.outer_index) < node.outer_index) && ((prev.track & Contrek::Finder::Node::IMAX) != 0)
131
+ start_right = node.abs_x_index
132
+ while (start_right += 1) != @vert_nodes[node.y].size
133
+ tnext = @vert_nodes[node.y][start_right]
134
+ if tnext.outer_index == cindex
135
+ if (tnext.track & Contrek::Finder::Node::IMIN) != 0
136
+ return [cindex, prev.inner_index]
137
+ else
138
+ return [-1, -1]
139
+ end
140
+ end
141
+ end
142
+ end
143
+ break if (start_left -= 1) < 0
144
+ end
145
+ end
146
+ [-1, -1]
147
+ end
148
+
149
+ def test_in_hole_o(node)
150
+ if node.outer_index > 0 && @vert_nodes[node.y].last != node
151
+ start_left = node.abs_x_index + 1
152
+ loop do
153
+ prev = @vert_nodes[node.y][start_left]
154
+ if ((cindex = prev.outer_index) < node.outer_index) && ((prev.track & Contrek::Finder::Node::IMIN) != 0)
155
+ start_right = node.abs_x_index
156
+ while (start_right -= 1) >= 0
157
+ tnext = @vert_nodes[node.y][start_right]
158
+ if tnext.outer_index == cindex
159
+ if (tnext.track & Contrek::Finder::Node::IMAX) != 0
160
+ return [cindex, prev.inner_index]
161
+ else
162
+ return [-1, -1]
163
+ end
164
+ end
165
+ end
166
+ end
167
+ break if (start_left += 1) == @vert_nodes[node.y].size
168
+ end
169
+ end
170
+ [-1, -1]
171
+ end
172
+
173
+ def draw_sequence(bitmap, val = nil)
174
+ count = 1
175
+ @sequence_coords.each do |coords|
176
+ bitmap.value_set(coords[:x], coords[:y], val.nil? ? count.alph : val)
177
+ count += 1
178
+ end
179
+ end
180
+
181
+ # inner way
182
+ # nodes in @plot_sequence
183
+ # coordinates in @sequence_coords
184
+ def plot_inner_node(node, versus, stop_at, start_node)
185
+ node.outer_index = start_node.outer_index
186
+ node.inner_index = stop_at.inner_index
187
+ @root_nodes.delete(node)
188
+ @inner_plot.delete(node)
189
+ last_node = @plot_sequence.last
190
+ next_node_nd = node.my_next(last_node, versus, :inner)
191
+ next_node = next_node_nd.node
192
+ @plot_sequence << node
193
+
194
+ plot = true
195
+ if next_node.y == last_node.y
196
+ plot = (node.tangs_sequence.send((versus == :a) ? :first : :last).node == next_node)
197
+ end
198
+ if plot
199
+ @sequence_coords << last_node.coords_entering_to(node, VERSUS_INVERTER[versus], Contrek::Finder::Node::INNER)
200
+ if node != start_node
201
+ if last_node.y == next_node.y
202
+ @sequence_coords << next_node.coords_entering_to(node, versus, Contrek::Finder::Node::INNER)
203
+ end
204
+ end
205
+ end
206
+
207
+ if node.track_uncomplete
208
+ @inner_new << node
209
+ else
210
+ @inner_new.delete(node)
211
+ end
212
+
213
+ return if next_node == stop_at
214
+ plot_inner_node(next_node, versus, stop_at, start_node)
215
+ end
216
+
217
+ # contour tracing core logic loop
218
+ def plot_node(node, start_node, versus = :a)
219
+ @root_nodes.delete(node)
220
+
221
+ node.outer_index = start_node.outer_index
222
+ last_node = @plot_sequence.last
223
+ next_node_nd = node.my_next(last_node, versus, :outer)
224
+ next_node = next_node_nd.node
225
+
226
+ @plot_sequence << node
227
+
228
+ plot = true
229
+ if next_node.y == last_node.y
230
+ plot = (node.tangs_sequence.send((versus == :a) ? :last : :first).node == next_node)
231
+ end
232
+
233
+ # coord
234
+ if plot
235
+ @sequence_coords << last_node.coords_entering_to(node, versus, Contrek::Finder::Node::OUTER)
236
+
237
+ if node != start_node
238
+ @inner_plot.contains(node) ? @inner_plot.delete(node) : @inner_plot << node
239
+ if last_node.y == next_node.y
240
+ @sequence_coords << next_node.coords_entering_to(node, VERSUS_INVERTER[versus], Contrek::Finder::Node::OUTER)
241
+ @inner_plot.contains(node) ? @inner_plot.delete(node) : @inner_plot << node
242
+ end
243
+ end
244
+ end
245
+ # exit if root_node
246
+
247
+ if node == start_node
248
+ return if node.track_complete
249
+ end
250
+ plot_node(next_node, start_node, versus)
251
+ end
252
+
253
+ def add_node(node)
254
+ @nodes += 1
255
+ node.abs_x_index = @vert_nodes[node.y].size
256
+
257
+ @vert_nodes[node.y] << node
258
+ @root_nodes << node
259
+
260
+ if node.y > 0
261
+ # all nodes untle up_node.max_x >= node.min_x
262
+ up_nodes = @vert_nodes[node.y - 1]
263
+ up_nodes_count = up_nodes.size
264
+ if up_nodes_count > 0
265
+ index = 0
266
+ loop do
267
+ up_node = up_nodes[index]
268
+ if up_node.max_x >= node.min_x
269
+ if up_node.min_x <= node.max_x
270
+ node.add_intersection(up_node)
271
+ up_node.add_intersection(node)
272
+ end
273
+ return if (index += 1) == up_nodes_count
274
+ loop do
275
+ up_node = up_nodes[index]
276
+ if up_node.min_x <= node.max_x
277
+ node.add_intersection(up_node)
278
+ up_node.add_intersection(node)
279
+ else
280
+ return
281
+ end
282
+ break if (index += 1) == up_nodes_count
283
+ end
284
+ return
285
+ end
286
+ break if (index += 1) == up_nodes_count
287
+ end
288
+ end
289
+
290
+ end
291
+ end
292
+ end
293
+ end
294
+ end
@@ -0,0 +1,121 @@
1
+ require "benchmark"
2
+ # todo
3
+ # - builtin png decoder which makes blocks to pass vert_nodes
4
+ # - work only in clockwise (no more :o e :a); finally for coords use push_back on :a and
5
+ # push_front on :o
6
+ module Contrek
7
+ module Finder
8
+ class PolygonFinder
9
+ NodeDescriptor = Struct.new(:node, :a, :o)
10
+ def initialize(bitmap, matcher, test_bitmap = nil, options = {})
11
+ @options = {versus: :a}.merge(options)
12
+ sanitize_options
13
+ @source_bitmap = bitmap
14
+ @matcher = matcher
15
+
16
+ @test_bitmap = test_bitmap
17
+ @node_cluster = NodeCluster.new(@source_bitmap.h, @options)
18
+ @reports = {}
19
+
20
+ # 1 finds matching blocks
21
+ @reports[:scan] = Benchmark.measure do
22
+ scan
23
+ end
24
+
25
+ # 2 builds relational spatially tree map
26
+ @reports[:build_tangs_sequence] = Benchmark.measure do
27
+ @node_cluster.build_tangs_sequence
28
+ end
29
+
30
+ # 3 plotting
31
+ @reports[:plot] = Benchmark.measure do
32
+ @node_cluster.plot(@test_bitmap)
33
+ end
34
+
35
+ # 4 compress
36
+ @reports[:compress] = Benchmark.measure do
37
+ if @options.has_key?(:compress)
38
+ @node_cluster.compress_coords
39
+ end
40
+ end
41
+ end
42
+
43
+ def sanitize_options
44
+ @options[:versus] = :a unless @options[:versus] == :a || @options[:versus] == :o
45
+ end
46
+
47
+ # infos
48
+ def process_info
49
+ {named_sequence: @node_cluster.sequences.map { |list| list.map(&:name).join }.join("-"),
50
+ groups: @node_cluster.sequences.size,
51
+ groups_names: @node_cluster.root_nodes.map(&:name).join,
52
+ polygons: @node_cluster.polygons,
53
+ benchmarks: format_benchmarks,
54
+ treemap: (@node_cluster.treemap if @options.has_key?(:treemap))}
55
+ end
56
+
57
+ def get_shapelines
58
+ shapes = []
59
+ @node_cluster.vert_nodes.each do |line|
60
+ line.each do |node|
61
+ shapes << {start_x: node.min_x, end_x: node.max_x, y: node.y}
62
+ end
63
+ end
64
+ shapes
65
+ end
66
+
67
+ def draw_polygons(png_image)
68
+ Contrek::Bitmaps::Painting.direct_draw_polygons(@node_cluster.polygons, png_image)
69
+ end
70
+
71
+ def draw_shapelines(png_image)
72
+ slines = get_shapelines
73
+ color = ChunkyPNG::Color("blue @ 1.0")
74
+ slines.each do |sline|
75
+ png_image.draw_line(sline[:start_x], sline[:y], sline[:end_x], sline[:y], color)
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ # image scan
82
+ def scan
83
+ last_color = nil
84
+ matching = false
85
+ min_x = 0
86
+ max_x = 0
87
+ @source_bitmap.scan do |x, y, color|
88
+ if @matcher.match?(color) && matching == false
89
+ min_x = x
90
+ last_color = color
91
+ matching = true
92
+ if x == (@source_bitmap.w - 1)
93
+ max_x = x
94
+ Contrek::Finder::Node.new(@node_cluster, min_x, max_x, y, last_color)
95
+ matching = false
96
+ end
97
+ elsif @matcher.unmatch?(color) && matching == true
98
+ max_x = x - 1
99
+ Contrek::Finder::Node.new(@node_cluster, min_x, max_x, y, last_color)
100
+ matching = false
101
+ elsif x == (@source_bitmap.w - 1) && matching == true
102
+ max_x = x
103
+ Contrek::Finder::Node.new(@node_cluster, min_x, max_x, y, last_color)
104
+ matching = false
105
+ end
106
+ end
107
+ end
108
+
109
+ def format_benchmarks
110
+ r = {}
111
+ total = 0
112
+ @reports.each do |k, v|
113
+ r[k] = (v.real * 1000).round(3)
114
+ total += v.real * 1000
115
+ end
116
+ r[:total] = total.round(3)
117
+ r
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,76 @@
1
+ module Map
2
+ class MercatorProjection
3
+ MERCATOR_RANGE = 256
4
+
5
+ class LatLng
6
+ attr_reader :lat, :lng
7
+ def initialize(lat:, lng:)
8
+ @lat = lat
9
+ @lng = lng
10
+ end
11
+ end
12
+
13
+ class Point
14
+ attr_accessor :x, :y
15
+ def initialize(x: 0, y: 0)
16
+ @x = x
17
+ @y = y
18
+ end
19
+ end
20
+
21
+ def initialize
22
+ @pixel_origin = Point.new(x: MERCATOR_RANGE / 2, y: MERCATOR_RANGE / 2)
23
+ @pixels_per_lon_degree = MERCATOR_RANGE.to_f / 360
24
+ @pixels_per_lon_radian = MERCATOR_RANGE.to_f / (2 * Math::PI)
25
+ end
26
+
27
+ def self.get_corners(center, zoom, map_width, map_height)
28
+ scale = 2**zoom
29
+ proj = Map::MercatorProjection.new
30
+ center_px = proj.from_lat_lng_to_point(lat_lng: center)
31
+ sw_point = Point.new(x: center_px.x - ((map_width / 2).to_f / scale), y: center_px.y + ((map_height / 2).to_f / scale))
32
+ sw_lat_lon = proj.from_point_to_lat_lng(sw_point)
33
+ ne_point = Point.new(x: center_px.x + ((map_width / 2).to_f / scale), y: center_px.y - ((map_height / 2).to_f / scale))
34
+ ne_lat_lon = proj.from_point_to_lat_lng(ne_point)
35
+ {
36
+ N: ne_lat_lon.lat,
37
+ E: ne_lat_lon.lng,
38
+ S: sw_lat_lon.lat,
39
+ W: sw_lat_lon.lng
40
+ }
41
+ end
42
+
43
+ def from_lat_lng_to_point(lat_lng:, opt_point: nil)
44
+ point = opt_point.nil? ? Point.new(x: 0, y: 0) : opt_point
45
+ origin = @pixel_origin
46
+ point.x = origin.x + (lat_lng.lng * @pixels_per_lon_degree)
47
+ siny = bound(Math.sin(degreesToRadians(lat_lng.lat)), -0.9999, 0.9999)
48
+ point.y = origin.y + (0.5 * Math.log((1 + siny) / (1 - siny)) * -@pixels_per_lon_radian)
49
+ point
50
+ end
51
+
52
+ def from_point_to_lat_lng(point)
53
+ origin = @pixel_origin
54
+ lng = (point.x - origin.x) / @pixels_per_lon_degree
55
+ lat_radians = (point.y - origin.y) / -@pixels_per_lon_radian
56
+ lat = radiansToDegrees(2 * Math.atan(Math.exp(lat_radians)) - (Math::PI / 2))
57
+ LatLng.new(lat: lat, lng: lng)
58
+ end
59
+
60
+ private
61
+
62
+ def degreesToRadians(deg)
63
+ deg * (Math::PI / 180)
64
+ end
65
+
66
+ def radiansToDegrees(rad)
67
+ rad / (Math::PI / 180)
68
+ end
69
+
70
+ def bound(value, opt_min, opt_max)
71
+ value = [value, opt_min].max if !opt_min.nil?
72
+ value = [value, opt_max].min if !opt_max.nil?
73
+ value
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,20 @@
1
+ module Contrek
2
+ module Matchers
3
+ class Matcher
4
+ attr_reader :values, :counters
5
+ def initialize(value)
6
+ @value_is = value
7
+ @values = []
8
+ @counters = {}
9
+ end
10
+
11
+ def match?(value)
12
+ @value_is == value
13
+ end
14
+
15
+ def unmatch?(value)
16
+ !match?(value)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Contrek
2
+ module Matchers
3
+ class MatcherHsb < Matcher
4
+ def match?(value)
5
+ if @values.index(value).nil?
6
+ @values << value
7
+ @counters[value] = {count: 1, match: 0}
8
+ else
9
+ @counters[value][:count] += 1
10
+ end
11
+
12
+ match = value[0].between?(@value_is[:h][:min], @value_is[:h][:max]) &&
13
+ value[1].between?(@value_is[:s][:min], @value_is[:s][:max]) &&
14
+ value[2].between?(@value_is[:b][:min], @value_is[:b][:max])
15
+
16
+ if match
17
+ @counters[value][:match] += 1
18
+ end
19
+
20
+ match
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ module Contrek
2
+ module Matchers
3
+ class ValueNotMatcher < Matcher
4
+ def match?(value)
5
+ @value_is != value
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ module Contrek
2
+ module Reducers
3
+ class LinearReducer < Reducer
4
+ def reduce!
5
+ start_p = @points[0]
6
+ end_p = @points[1]
7
+ dir = seq_dir(start_p, end_p)
8
+ @points[2..].map.with_index do |point, i|
9
+ if (act_seq = seq_dir(end_p, point)) == dir
10
+ @points.delete_at(@points.index(end_p))
11
+ else
12
+ dir = act_seq
13
+ end
14
+ end_p = point
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def seq_dir(a, b)
21
+ [b[:x] - a[:x], b[:y] - a[:y]]
22
+ end
23
+ end
24
+ end
25
+ end