require 'yaml' module Mork # The Grid is a set of expectations on what the response sheet should look like # It knows nothing about the actual scanned image # All returned values are in the arbitrary units given in the configuration file class Grid def initialize(type = :default, fname="config/grids.yml") # from the full YAML file, only get the requested grid type c = YAML.load_file(fname) @params = c[type.to_s] end def set_page_size(x, y) @px = x.to_f @py = y.to_f end def default_chlist [@params["responses"]["max_cells"]] * max_questions end def max_questions columns * rows end def max_choices_per_question @params["responses"]["max_cells"] end def code_bits CODE_BITS end def header @params["header"] end def reg_mark_search_area(n) rmp = REG_MARGIN + REG_SEARCH rmm = REG_MARGIN - REG_SEARCH case n when :top_left reg_mark_sa rmm, rmm when :top_right reg_mark_sa page_width - rmp, rmm when :bottom_right reg_mark_sa page_width - rmp, page_height - rmp when :bottom_left reg_mark_sa rmm, page_height - rmp end end def reg_mark_sa(x, y) cw = @px / page_width ch = @py / page_height { x: (cw * x).round, y: (ch * y).round, w: (cw * REG_SEARCH * 2).round, h: (ch * REG_SEARCH * 2).round } end def choice_cell_area(q, c) { x: (cx * cell_x(q, c)).round, y: (cy * cell_y(q) ).round, w: (cx * cell_width ).round, h: (cy * cell_height ).round } end def white_calibration_area code_cell_area -1 end def black_calibration_area code_cell_area 0 end def code_bit_area(i) code_cell_area i+1 end # ============ # = to Prawn = # ============ def pdf_page_size [page_width.mm, page_height.mm] end def pdf_margins REG_MARGIN.mm end def pdf_reg_marks r = REG_RADIUS.mm [ { p: [0, 0 ], r: r }, { p: [0, reg_frame_height.mm], r: r }, { p: [reg_frame_width.mm, reg_frame_height.mm], r: r }, { p: [reg_frame_width.mm, 0 ], r: r } ] end def pdf_dark_calibration_area pdf_code_cell_area 0 end def pdf_code_areas_for(code) a = [] CODE_BITS.times do |bit| a << pdf_code_cell_area(bit+1) if code[bit] == 1 end a end def pdf_code_bit_areas (1..CODE_BITS).collect do |bit| pdf_code_cell_area bit end end def pdf_code_cell_area(i) { p: [code_cell_x(i).mm, (reg_frame_height - code_y).mm], w: CODE_WIDTH.mm, h: CODE_HEIGHT.mm * 2 } end def pdf_choice_cell_area(q, c) { p: [cell_x(q, c).mm, (reg_frame_height - cell_y(q)).mm], w: cell_width.mm, h: cell_height.mm } end def pdf_choice_letter_xy(q, c) [ cell_x(q, c).mm + 2.mm, (reg_frame_height - cell_y(q)).mm - 3.mm ] end def pdf_qnum_xy(q) [ cell_x(q, 0).mm - pdf_qnum_width - Q_NUM_GAP.mm, (reg_frame_height - cell_y(q)).mm - 0.5.mm ] end def pdf_qnum_width Q_NUM_WIDTH.mm end def pdf_header_xy(k) [ header[k.to_s]["left"].to_f.mm, (reg_frame_height - header[k.to_s]["top"].to_f).mm ] end def pdf_header_width(k) header[k.to_s]["width"].to_f.mm end def pdf_header_size(k) header[k.to_s]["size"].to_f end private def cx @px / reg_frame_width end def cy @py / reg_frame_height end # x, y = cell_xy(q,c) # # the distances from the registration frame of the left and top edges # of the c-th choice cell of the q-th question def cell_xy(q, c) return cell_x(q, c), cell_y(q) end # cell_y(q) # # the distance from the registration frame to the top edge # of all choice cells in the q-th question def cell_y(q) first_y + response_spacing * (q % rows) - cell_height / 2 end # cell_x(q,c) # # the distance from the registration frame to the left edge # of the c-th choice cell of the q-th question def cell_x(q,c) first_x + column_width * (q / rows) + cell_spacing * c - cell_width / 2 end def cell_width @params["responses"]["cell_width"].to_f end def cell_height @params["responses"]["cell_height"].to_f end def cell_spacing @params["responses"]["x_spacing"].to_f end def response_spacing @params["responses"]["y_spacing"].to_f end def column_width @params["responses"]["column_width"].to_f end def row_spacing @params["responses"]["y_spacing"].to_f end def first_x @params["responses"]["first_x"].to_f end def first_y @params["responses"]["first_y"].to_f end def rows @params["responses"]["rows"] end def columns @params["responses"]["columns"] end # ============== # = sheet code = # ============== def code_cell_area(i) { x: (cx * code_cell_x(i) ).round, y: (cy * code_y ).round, w: (cx * CODE_WIDTH ).round, h: (cy * CODE_HEIGHT).round } end def code_cell_x(i) CODE_LEFT + CODE_SPACING * i end def code_y reg_frame_height - CODE_HEIGHT end # ====================== # = registration sides = # ====================== def reg_frame_width page_width - REG_MARGIN * 2 end def reg_frame_height page_height - REG_MARGIN * 2 end def page_width @params["page_size"]["width"].to_f end def page_height @params["page_size"]["height"].to_f end # ============ # = Header # ============ def name_x @params["header"]["name"]["top"].to_f end def name_y @params["header"]["name"]["left"].to_f end def name_w @params["header"]["name"]["width"].to_f end def name_size @params["header"]["name"]["size"] end end end