ruby_marks 0.1.5 → 0.2.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.
@@ -3,9 +3,9 @@ module RubyMarks
3
3
 
4
4
  class Recognizer
5
5
 
6
- attr_reader :file, :raised_watchers, :groups, :watchers, :file_str
6
+ attr_reader :file, :raised_watchers, :groups, :watchers, :file_str, :original_file_str
7
+ attr_accessor :config
7
8
 
8
- attr_accessor :current_position, :clock_marks, :config
9
9
 
10
10
  def initialize
11
11
  self.reset_document
@@ -13,14 +13,23 @@ module RubyMarks
13
13
  self.create_config
14
14
  end
15
15
 
16
+
16
17
  def file=(file)
17
18
  self.reset_document
18
19
  @file = nil
19
20
  @file_str = nil
20
21
  @file = Magick::Image.read(file).first
21
- @file = @file.threshold(@config.calculated_threshold_level)
22
+ @file = @file.quantize(256, Magick::GRAYColorspace)
23
+ @file = @file.threshold(@config.calculated_threshold_level)
24
+ @original_file = @file
25
+ @file = @file.edge(@config.edge_level)
26
+
27
+ @groups.each_pair do |label, group|
28
+ group.marks = Hash.new { |hash, key| hash[key] = [] }
29
+ end
22
30
  end
23
31
 
32
+
24
33
  def reset_document
25
34
  @current_position = {x: 0, y: 0}
26
35
  @clock_marks = []
@@ -28,28 +37,34 @@ module RubyMarks
28
37
  @watchers = {}
29
38
  end
30
39
 
40
+
31
41
  def create_config
32
42
  @config ||= RubyMarks::Config.new(self)
33
43
  end
34
44
 
45
+
35
46
  def filename
36
47
  @file && @file.filename
37
48
  end
38
49
 
50
+
39
51
  def configure(&block)
40
52
  self.create_config
41
53
  @config.configure(&block)
42
54
  end
43
55
 
56
+
44
57
  def add_group(group)
45
58
  @groups[group.label] = group if group
46
59
  end
47
60
 
61
+
48
62
  def add_watcher(watcher_name, &block)
49
63
  watcher = RubyMarks::Watcher.new(watcher_name, self, &block)
50
64
  @watchers[watcher.name] = watcher if watcher
51
65
  end
52
66
 
67
+
53
68
  def raise_watcher(name, *args)
54
69
  watcher = @watchers[name]
55
70
  if watcher
@@ -59,406 +74,380 @@ module RubyMarks
59
74
  end
60
75
  end
61
76
 
62
- def move_to(x, y)
63
- @current_position = {x: @current_position[:x] + x, y: @current_position[:y] + y}
64
- end
65
77
 
66
- def marked?(expected_width, expected_height)
78
+ def scan
67
79
  raise IOError, "There's a invalid or missing file" if @file.nil?
68
-
69
- self.export_file_to_str if self.file_str.nil?
70
80
 
71
- if self.current_position
81
+ unmarked_group_found = false
82
+ multiple_marked_found = false
72
83
 
73
- neighborhood_x = current_position[:x]-1..current_position[:x]+1
74
- neighborhood_y = current_position[:y]-1..current_position[:y]+1
84
+ result = Hash.new { |hash, key| hash[key] = [] }
85
+ result.tap do |result|
86
+
87
+ self.detect_groups
88
+
89
+ @groups.each_pair do |label, group|
90
+ marks = Hash.new { |hash, key| hash[key] = [] }
91
+ group.marks.each_pair do |line, value|
92
+ value.each do |mark|
93
+ marks[line] << mark.value if mark.marked?
94
+ end
95
+ end
96
+
97
+ if marks.any?
98
+ result[group.label.to_sym] = marks
99
+ multiple_marked_found = true if marks.size > 1
100
+ else
101
+ unmarked_group_found = true
102
+ end
103
+ end
75
104
 
76
- neighborhood_y.each do |current_y|
77
- neighborhood_x.each do |current_x|
78
- position = self.file_str[current_y][current_x]
79
-
80
- if position == "."
105
+ raise_watcher :scan_unmarked_watcher, result if unmarked_group_found
106
+ raise_watcher :scan_multiple_marked_watcher, result if multiple_marked_found
107
+ raise_watcher :scan_mark_watcher, result, unmarked_group_found, multiple_marked_found if unmarked_group_found || multiple_marked_found
108
+ end
109
+ end
81
110
 
82
- stack = flood_scan(current_x, current_y)
83
111
 
84
- x_elements = []
85
- y_elements = []
86
- stack.each do |k,v|
87
- stack[k].inject(x_elements, :<<)
88
- y_elements << k
89
- end
112
+ def detect_groups
113
+ file_str = RubyMarks::ImageUtils.export_file_to_str(@file)
114
+ original_file_str = RubyMarks::ImageUtils.export_file_to_str(@original_file)
115
+ incorrect_bubble_line_found = false
116
+ bubbles_adjusted = false
117
+ incorrect_expected_lines = false
118
+ @groups.each_pair do |label, group|
119
+ next unless group.expected_coordinates.any?
90
120
 
91
- x_elements.sort!.uniq!
92
- y_elements.sort!.uniq!
121
+ group_center = RubyMarks::ImageUtils.image_center(group.expected_coordinates)
93
122
 
94
- x1 = x_elements.first || 0
95
- x2 = x_elements.last || 0
96
- y1 = y_elements.first || 0
97
- y2 = y_elements.last || 0
123
+ block = find_block_marks(file_str, group_center[:x], group_center[:y], group.expected_coordinates)
98
124
 
99
- current_width = RubyMarks::ImageUtils.calc_width(x1, x2)
100
- current_height = RubyMarks::ImageUtils.calc_height(y1, y2)
125
+ if block
126
+ group.coordinates = {x1: block[:x1], x2: block[:x2], y1: block[:y1], y2: block[:y2]}
101
127
 
102
- if current_width >= expected_width + 2
103
- distance_x1 = current_x - x1
104
- distance_x2 = x2 - current_x
128
+ marks_blocks = find_marks(original_file_str, group)
129
+ positions = []
130
+ marks_blocks.each do |mark|
131
+ line = 0
132
+ mark_width = RubyMarks::ImageUtils.calc_width(mark[:x1], mark[:x2])
133
+ mark_height = RubyMarks::ImageUtils.calc_height(mark[:y1], mark[:y2])
134
+
135
+ if mark_width >= group.mark_width_with_down_tolerance &&
136
+ mark_width <= group.mark_width_with_up_tolerance &&
137
+ mark_height >= group.mark_height_with_down_tolerance &&
138
+ mark_height <= group.mark_height_with_up_tolerance
139
+
140
+ group.marks.each_pair do |key, marks_array|
141
+ mark_positions = mark[:y1]-10..mark[:y1]+10
142
+
143
+ marks_array.each do |m|
144
+ if mark_positions.include?(m.coordinates[:y1])
145
+ line = key
146
+ break
147
+ end
148
+ end
105
149
 
106
- if distance_x1 <= distance_x2
107
- x2 = x1 + expected_width
108
- else
109
- x1 = x2 - expected_width
110
- end
111
- current_width = RubyMarks::ImageUtils.calc_width(x1, x2)
150
+ break if line > 0
112
151
  end
113
152
 
153
+ line = group.marks.size + 1 if line == 0
114
154
 
115
- if current_height >= expected_height + 2
116
- distance_y1 = current_y - y1
117
- distance_y2 = y2 - current_y
118
-
119
- if distance_y1 <= distance_y2
120
- y2 = y1 + expected_height
121
- else
122
- y1 = y2 - expected_height
123
- end
124
- current_height = RubyMarks::ImageUtils.calc_height(y1, y2)
155
+ conflict_marks = group.marks[line].select do |el|
156
+ el.coordinates[:x2] >= mark[:x1] && el.coordinates[:x2] <= mark[:x2] ||
157
+ el.coordinates[:x1] >= mark[:x1] && el.coordinates[:x1] <= mark[:x2]
125
158
  end
126
159
 
127
- if (current_width >= expected_width - 4 && current_width <= expected_width + 4) &&
128
- (current_height >= expected_height - 4 && current_height <= expected_height + 4)
129
-
130
-
131
- colors = []
132
-
133
- x_pos = x1..x2
134
- y_pos = y1..y2
135
-
136
- y_pos.each do |y|
137
- x_pos.each do |x|
138
- color = self.file_str[y][x]
139
- colors << color
140
- end
160
+ if conflict_marks.any?
161
+ conflict_marks.each do |conflict_mark|
162
+ group.marks[line].delete(conflict_mark)
141
163
  end
164
+ else
165
+ mark_file = @original_file.crop(mark[:x1], mark[:y1], mark_width, mark_height)
166
+
167
+ mark = RubyMarks::Mark.new group: group,
168
+ coordinates: {x1: mark[:x1], y1: mark[:y1], x2: mark[:x2], y2: mark[:y2]},
169
+ image_str: RubyMarks::ImageUtils.export_file_to_str(mark_file),
170
+ line: line
142
171
 
143
- intensity = colors.count(".") * 100 / colors.size
144
- return true if intensity >= @config.intensity_percentual
172
+ group.marks[line] << mark
173
+ group.marks[line].sort! { |a, b| a.coordinates[:x1] <=> b.coordinates[:x1] }
145
174
  end
146
175
  end
147
-
148
176
  end
149
- end
150
- end
151
-
152
- return false
153
- end
154
-
155
- def unmarked?(x_pos, y_pos)
156
- !marked?(x_pos, y_pos)
157
- end
158
-
159
- def scan
160
- raise IOError, "There's a invalid or missing file" if @file.nil?
177
+
178
+ first_position = 0
179
+ elements_position_count = 0
180
+ group.marks.each_pair do |line, marks|
161
181
 
162
- self.export_file_to_str if self.file_str.nil?
182
+ if marks.count == group.marks_options.count &&
183
+ marks.first && marks.first.coordinates
163
184
 
164
- unmarked_group_found = false
165
- multiple_marked_found = false
185
+ first_position += marks.first.coordinates[:x1]
166
186
 
167
- result = {}
168
- result.tap do |result|
169
- position_before = @current_position
170
- scan_clock_marks unless clock_marks.any?
171
-
172
- return false if self.config.expected_clocks_count > 0 && @clock_marks.count != self.config.expected_clocks_count
173
- clock_marks.each_with_index do |clock_mark, index|
174
- group_hash = {}
175
- @groups.each do |key, group|
176
- if group.belongs_to_clock?(index + 1)
177
- @current_position = {x: clock_mark.coordinates[:x2], y: clock_mark.vertical_middle_position}
178
- move_to(group.x_distance_from_clock, 0)
179
- markeds = []
180
- group.marks_options.each do |mark|
181
- markeds << mark if marked?(group.mark_width, group.mark_height)
182
- move_to(group.distance_between_marks, 0)
183
- end
184
- if markeds.any?
185
- group_hash["group_#{key}".to_sym] = markeds
186
- multiple_marked_found = true if markeds.size > 1
187
- else
188
- unmarked_group_found = true
189
- end
187
+ elements_position_count += 1
188
+ else
189
+ incorrect_bubble_line_found = true
190
190
  end
191
191
  end
192
- result["clock_#{index+1}".to_sym] = group_hash if group_hash.any?
193
- end
194
- @current_position = position_before
195
- raise_watcher :scan_unmarked_watcher, result if unmarked_group_found
196
- raise_watcher :scan_multiple_marked_watcher, result if multiple_marked_found
197
- raise_watcher :scan_mark_watcher, result, unmarked_group_found, multiple_marked_found if unmarked_group_found || multiple_marked_found
198
- end
199
- end
200
192
 
201
- def flag_position
202
-
203
- raise IOError, "There's a invalid or missing file" if @file.nil?
193
+ if @config.adjust_inconsistent_bubbles && elements_position_count > 0
194
+ first_position = first_position / elements_position_count
195
+ distance = group.distance_between_marks * (group.marks_options.count - 1)
196
+ last_position = first_position + distance
197
+
198
+ group.marks.each_pair do |line, marks|
199
+ loop do
200
+ reprocess = false
201
+ marks.each_with_index do |current_mark, index|
202
+ if current_mark.coordinates[:x1] < first_position - 10 ||
203
+ current_mark.coordinates[:x1] > last_position + 10
204
+
205
+ group.marks[line].delete(current_mark)
206
+ reprocess = true
207
+ bubbles_adjusted = true
208
+ break
209
+
210
+ else
211
+
212
+ if index == 0
213
+ first_mark_position = first_position-5..first_position+5
214
+ unless first_mark_position.include?(current_mark.coordinates[:x1])
215
+ new_mark_x1 = first_position
216
+ new_mark_x2 = new_mark_x1 + group.mark_width
217
+ new_mark_y1 = current_mark.coordinates[:y1]
218
+ new_mark_y2 = new_mark_y1 + group.mark_height
219
+ reprocess = true
220
+ end
221
+ end
222
+
223
+ unless reprocess
224
+ next_mark = marks[index + 1]
225
+ distance = 0
226
+ distance = next_mark.coordinates[:x1] - current_mark.coordinates[:x1] if next_mark
227
+
228
+ if distance > group.distance_between_marks + 10 ||
229
+ next_mark.nil? && index + 1 < group.marks_options.count
230
+ new_mark_x1 = current_mark.coordinates[:x1] + group.distance_between_marks
231
+ new_mark_x2 = new_mark_x1 + group.mark_width
232
+ new_mark_y1 = current_mark.coordinates[:y1]
233
+ new_mark_y2 = new_mark_y1 + group.mark_height
234
+ end
235
+ end
236
+
237
+ if new_mark_x1 && new_mark_x2 && new_mark_y1 && new_mark_y2
238
+ mark_width = RubyMarks::ImageUtils.calc_width(new_mark_x1, new_mark_x2)
239
+ mark_height = RubyMarks::ImageUtils.calc_height(new_mark_y1, new_mark_y2)
240
+
241
+ mark_file = @original_file.crop(new_mark_x1, new_mark_y1, mark_width, mark_height)
242
+
243
+ current_mark = RubyMarks::Mark.new group: group,
244
+ coordinates: {x1: new_mark_x1, y1: new_mark_y1, x2: new_mark_x2, y2: new_mark_y2},
245
+ image_str: RubyMarks::ImageUtils.export_file_to_str(mark_file),
246
+ line: line
247
+
248
+ group.marks[line] << current_mark
249
+ group.marks[line].sort! { |a, b| a.coordinates[:x1] <=> b.coordinates[:x1] }
250
+ reprocess = true
251
+ bubbles_adjusted = true
252
+ break
253
+ end
254
+ end
255
+ break if reprocess
256
+ end
257
+ break unless reprocess
258
+ end
204
259
 
205
- file = @file.dup
260
+ incorrect_expected_lines = true if group.incorrect_expected_lines
261
+ end
262
+ end
206
263
 
207
- file.tap do |file|
208
- if current_position
209
- add_mark file
210
264
  end
265
+ end
266
+
267
+ if incorrect_bubble_line_found || bubbles_adjusted || incorrect_expected_lines
268
+ raise_watcher :incorrect_group_watcher, incorrect_expected_lines, incorrect_bubble_line_found, bubbles_adjusted
211
269
  end
212
270
  end
213
271
 
214
- def flag_all_marks
215
272
 
216
- raise IOError, "There's a invalid or missing file" if @file.nil?
217
-
218
- self.export_file_to_str if self.file_str.nil?
273
+ def find_block_marks(image, x, y, expected_coordinates)
274
+ found_blocks = []
275
+ expected_width = RubyMarks::ImageUtils.calc_width(expected_coordinates[:x1], expected_coordinates[:x2])
276
+ expected_height = RubyMarks::ImageUtils.calc_height(expected_coordinates[:y1], expected_coordinates[:y2])
277
+ block = nil
278
+ while x <= expected_coordinates[:x2] && y <= expected_coordinates[:y2]
279
+ if image[y] && image[y][x] == " "
280
+ block = find_in_blocks(found_blocks, x, y)
281
+ unless block
282
+ block = find_block(image, x, y)
283
+ found_blocks << block
284
+
285
+ block[:width] = RubyMarks::ImageUtils.calc_width(block[:x1], block[:x2])
286
+ block[:height] = RubyMarks::ImageUtils.calc_height(block[:y1], block[:y2])
219
287
 
220
- file = @file.dup
288
+ block_width_with_tolerance = block[:width] + 100
289
+ block_height_with_tolerance = block[:height] + 100
221
290
 
222
- file.tap do |file|
223
- position_before = @current_position
224
-
225
- dr = Magick::Draw.new
226
- dr.fill(RubyMarks::COLORS[4])
227
- dr.line(@config.clock_marks_scan_x, 0, @config.clock_marks_scan_x, file.page.height)
228
- dr.draw(file)
229
291
 
230
- scan_clock_marks unless clock_marks.any?
231
-
232
- clock_marks.each_with_index do |clock, index|
233
- dr = Magick::Draw.new
234
- dr.fill(RubyMarks::COLORS[5])
235
- dr.rectangle(clock.coordinates[:x1], clock.coordinates[:y1], clock.coordinates[:x2], clock.coordinates[:y2])
236
- dr.draw(file)
237
- end
292
+ return block if block_width_with_tolerance >= expected_width &&
293
+ block_height_with_tolerance >= expected_height
238
294
 
239
- clock_marks.each_with_index do |clock_mark, index|
240
- @groups.each do |key, group|
241
- if group.belongs_to_clock?(index + 1)
242
- @current_position = {x: clock_mark.coordinates[:x2], y: clock_mark.vertical_middle_position}
243
- move_to(group.x_distance_from_clock, 0)
244
- group.marks_options.each do |mark|
245
- add_mark file
246
- move_to(group.distance_between_marks, 0)
247
- end
248
- end
249
295
  end
250
296
  end
251
297
 
252
- @current_position = position_before
298
+ x += 1
299
+ y += 1
253
300
  end
254
301
  end
255
302
 
256
- def scan_clock_marks
257
-
258
- raise IOError, "There's a invalid or missing file" if @file.nil?
259
-
260
- self.export_file_to_str if self.file_str.nil?
261
-
262
- @clock_marks = []
263
- x = @config.clock_marks_scan_x
264
- total_width = @file && @file.page.width || 0
265
- total_height = @file && @file.page.height || 0
266
-
267
- @clock_marks.tap do |clock_marks|
268
- current_y = 0
269
- loop do
270
-
271
- break if current_y >= total_height
272
303
 
273
- position = @file_str[current_y][x]
274
-
275
- if position == "."
276
- stack = flood_scan(x, current_y)
277
-
278
- x_elements = []
279
- y_elements = []
280
- stack.each do |k,v|
281
- stack[k].inject(x_elements, :<<)
282
- y_elements << k
304
+ def find_marks(image, group)
305
+ block = group.coordinates
306
+ y = block[:y1]
307
+ blocks = []
308
+ blocks.tap do |blocks|
309
+ while y < block[:y2]
310
+ x = block[:x1]
311
+ while x < block[:x2] do
312
+ if image[y][x] == " "
313
+ x += 1
314
+ next
283
315
  end
284
316
 
285
- x_elements.sort!.uniq!
286
- y_elements.sort!.uniq!
287
- last_y = y_elements.last
288
-
289
- if x_elements.size > 50
290
- current_y = last_y + 1
291
- next
292
- end
317
+ result = find_in_blocks(blocks, x, y)
318
+ unless result
319
+ result = find_block(image, x, y, ".", block)
293
320
 
294
- loop do
295
- stack_modified = false
321
+ mark_width = RubyMarks::ImageUtils.calc_width(result[:x1], result[:x2])
322
+ mark_height = RubyMarks::ImageUtils.calc_height(result[:y1], result[:y2])
296
323
 
297
324
 
298
- x_elements.each do |col|
299
- element_count = 0
300
- y_elements.each do |row|
301
- element_count += 1 if stack[row].include?(col)
302
- end
303
-
304
- if element_count > 0 && element_count < self.config.clock_height_with_down_tolerance
305
- current_width = RubyMarks::ImageUtils.calc_width(x_elements.first, x_elements.last)
306
- middle = RubyMarks::ImageUtils.calc_middle_horizontal(x_elements.first, current_width)
307
-
308
- x_elements.delete_if do |el|
309
- col <= middle && el <= col || col >= middle && el >= col
310
- end
311
-
312
- stack_modified = true
325
+ if mark_width > group.mark_width_with_up_tolerance
326
+ distance_x1 = x - result[:x1]
327
+ distance_x2 = result[:x2] - x
328
+ if distance_x1 <= distance_x2
329
+ result[:x2] = result[:x1] + group.mark_width_with_up_tolerance - 2
330
+ else
331
+ result[:x1] = result[:x2] - group.mark_width_with_up_tolerance + 2
313
332
  end
314
- end
315
-
316
- y_elements.each do |row|
317
- if stack[row].count < self.config.clock_width_with_down_tolerance
318
- current_height = RubyMarks::ImageUtils.calc_height(y_elements.first, y_elements.last)
319
- middle = RubyMarks::ImageUtils.calc_middle_vertical(y_elements.first, current_height)
333
+ end
320
334
 
321
- y_elements.delete_if do |ln|
322
- row <= middle && ln <= row || row >= middle && ln >= row
323
- end
324
-
325
- stack_modified = true
326
- end
335
+ if mark_height > group.mark_height_with_up_tolerance
336
+ distance_y1 = y - result[:y1]
337
+ distance_y2 = result[:y2] - y
338
+ if distance_y1 <= distance_y2
339
+ result[:y2] = result[:y1] + group.mark_height_with_up_tolerance - 2
340
+ else
341
+ result[:y1] = result[:y2] - group.mark_height_with_up_tolerance + 2
342
+ end
327
343
  end
328
344
 
329
- break unless stack_modified
345
+ blocks << result
330
346
  end
331
-
332
- x1 = x_elements.first || 0
333
- x2 = x_elements.last || 0
334
- y1 = y_elements.first || 0
335
- y2 = y_elements.last || 0
347
+ x += 1
336
348
  end
349
+ y += 1
350
+ end
351
+ end
352
+ end
337
353
 
338
- clock = RubyMarks::ClockMark.new(recognizer: self, coordinates: {x1: x1, x2: x2, y1: y1, y2: y2})
339
354
 
340
- if clock.valid?
341
- clock_marks << clock
342
- current_y = last_y
343
- end
355
+ def flag_position(position)
356
+ raise IOError, "There's a invalid or missing file" if @file.nil?
344
357
 
345
- current_y += 1
346
- end
347
- raise_watcher :clock_mark_difference_watcher if self.config.expected_clocks_count > 0 && @clock_marks.count != self.config.expected_clocks_count
358
+ file = @original_file.dup
359
+
360
+ file.tap do |file|
361
+ add_mark file, position
348
362
  end
349
363
  end
350
364
 
351
- def flood_scan(x, y)
352
-
353
- result_mask = Hash.new { |hash, key| hash[key] = [] }
354
- result_mask.tap do |result_mask|
355
- process_queue = Hash.new { |hash, key| hash[key] = [] }
356
- process_line = true
357
-
358
- loop do
359
-
360
- break if y > self.file_str.size - 1
361
- reset_process = false
362
-
363
- if process_line
364
- current_x = x.to_i
365
- loop do
366
- position = self.file_str[y][current_x]
367
-
368
- break if position != "." || current_x - 1 <= 0
369
- process_queue[y] << current_x unless process_queue[y].include?(current_x) || result_mask[y].include?(current_x)
370
- result_mask[y] << current_x unless result_mask[y].include?(current_x)
371
- current_x = current_x - 1
372
- end
373
365
 
374
- current_x = x.to_i
375
- loop do
376
- position = self.file_str[y][current_x]
366
+ def flag_all_marks
367
+ raise IOError, "There's a invalid or missing file" if @file.nil?
368
+
369
+ file = @original_file.dup
377
370
 
378
- break if position != "." || current_x + 1 >= self.file.page.width
379
- process_queue[y] << current_x unless process_queue[y].include?(current_x) || result_mask[y].include?(current_x)
380
- result_mask[y] << current_x unless result_mask[y].include?(current_x)
381
- current_x = current_x + 1
382
- end
371
+ file.tap do |file|
383
372
 
384
- result_mask[y] = result_mask[y].sort
385
- process_queue[y] = process_queue[y].sort
386
- end
373
+ self.detect_groups
387
374
 
388
- if process_queue[y].size > 50
389
- process_queue = Hash.new { |hash, key| hash[key] = [] }
390
- y = y + 1
391
- next
392
- end
375
+ @groups.each_pair do |label, group|
393
376
 
394
- process_line = true
377
+ dr = Magick::Draw.new
378
+ dr.stroke_width = 5
379
+ dr.stroke(RubyMarks::COLORS[3])
380
+ dr.line(group.expected_coordinates[:x1], group.expected_coordinates[:y1], group.expected_coordinates[:x2], group.expected_coordinates[:y1])
381
+ dr.line(group.expected_coordinates[:x2], group.expected_coordinates[:y1], group.expected_coordinates[:x2], group.expected_coordinates[:y2])
382
+ dr.line(group.expected_coordinates[:x2], group.expected_coordinates[:y2], group.expected_coordinates[:x1], group.expected_coordinates[:y2])
383
+ dr.line(group.expected_coordinates[:x1], group.expected_coordinates[:y2], group.expected_coordinates[:x1], group.expected_coordinates[:y1])
384
+ dr.draw(file)
395
385
 
396
- process_queue[y].each do |element|
397
- if y - 1 >= 0
398
- position = self.file_str[y-1][element]
399
-
400
- if position == "." && !result_mask[y-1].include?(element)
401
- x = element
402
- y = y - 1
403
- reset_process = true
404
- break
405
- end
406
- end
386
+ if group.coordinates
387
+ dr = Magick::Draw.new
388
+ dr.stroke_width = 5
389
+ dr.stroke(RubyMarks::COLORS[5])
390
+ dr.line(group.coordinates[:x1], group.coordinates[:y1], group.coordinates[:x2], group.coordinates[:y1])
391
+ dr.line(group.coordinates[:x2], group.coordinates[:y1], group.coordinates[:x2], group.coordinates[:y2])
392
+ dr.line(group.coordinates[:x2], group.coordinates[:y2], group.coordinates[:x1], group.coordinates[:y2])
393
+ dr.line(group.coordinates[:x1], group.coordinates[:y2], group.coordinates[:x1], group.coordinates[:y1])
394
+ dr.draw(file)
407
395
  end
408
396
 
409
- next if reset_process
410
-
411
- process_queue[y].each do |element|
412
- if y + 1 <= self.file.page.height
413
- position = self.file_str[y+1][element]
414
-
415
- if position == "." && !result_mask[y+1].include?(element)
416
- x = element
417
- y = y + 1
418
- reset_process = true
419
- break
420
- else
421
- process_queue[y].delete(element)
422
- end
397
+ marks = Hash.new { |hash, key| hash[key] = [] }
398
+ group.marks.each_pair do |line, value|
399
+ value.each do |mark|
400
+ add_mark file, RubyMarks::ImageUtils.image_center(mark.coordinates)
423
401
  end
424
402
  end
403
+ end
404
+ end
405
+ end
425
406
 
426
- next if reset_process
427
407
 
428
- process_queue.each do |k,v|
429
- process_queue.delete(k) if v.empty?
430
- end
408
+ private
409
+ def find_block(image, x, y, character=" ", coordinates={})
410
+ stack = RubyMarks::ImageUtils.flood_scan(image, x, y, character, coordinates)
411
+
412
+ x_elements = []
413
+ y_elements = []
414
+ stack.each do |k,v|
415
+ stack[k].inject(x_elements, :<<)
416
+ y_elements << k
417
+ end
431
418
 
432
- break if process_queue.empty?
419
+ x_elements.sort!.uniq!
420
+ y_elements.sort!.uniq!
433
421
 
434
- process_line = false
435
- y = process_queue.first[0] if process_queue.first.is_a?(Array)
436
- end
437
- end
422
+ x1 = x_elements.first || 0
423
+ x2 = x_elements.last || 0
424
+ y1 = y_elements.first || 0
425
+ y2 = y_elements.last || 0
426
+
427
+ {x1: x1, x2: x2, y1: y1, y2: y2}
438
428
  end
439
429
 
440
- def export_file_to_str
441
- @file_str = @file.export_pixels_to_str
442
- @file_str = @file_str.gsub!(Regexp.new('\xFF\xFF\xFF', nil, 'n'), " ,")
443
- @file_str = @file_str.gsub!(Regexp.new('\x00\x00\x00', nil, 'n'), ".,")
444
- @file_str = @file_str.split(',')
445
- @file_str = @file_str.each_slice(@file.page.width).to_a
430
+
431
+ def find_in_blocks(blocks, x, y)
432
+ blocks.find do |result|
433
+ result[:x1] <= x && result[:x2] >= x &&
434
+ result[:y1] <= y && result[:y2] >= y
435
+ end
446
436
  end
447
437
 
448
- private
449
- def add_mark(file)
438
+ def add_mark(file, position)
450
439
  dr = Magick::Draw.new
451
- dr.annotate(file, 0, 0, current_position[:x]-9, current_position[:y]+11, "+") do
440
+ dr.annotate(file, 0, 0, position[:x]-9, position[:y]+11, "+") do
452
441
  self.pointsize = 30
453
442
  self.fill = '#900000'
454
443
  end
455
444
 
456
445
  dr = Magick::Draw.new
457
446
  dr.fill = '#FF0000'
458
- dr.point(current_position[:x], current_position[:y])
459
- dr.point(current_position[:x] + 1, current_position[:y])
460
- dr.point(current_position[:x], current_position[:y] + 1)
461
- dr.point(current_position[:x] + 1, current_position[:y] + 1)
447
+ dr.point(position[:x], position[:y])
448
+ dr.point(position[:x], position[:y] + 1)
449
+ dr.point(position[:x] + 1, position[:y])
450
+ dr.point(position[:x] + 1, position[:y] + 1)
462
451
  dr.draw(file)
463
452
  end
464
453