ruby_marks 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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