indexing description: "Factory to cut images into a puzzle" author: "Stefan Hildenbrand and Team UC" date: "$Date$" revision: "$Revision$" class QUAD_CUTTER inherit EM_SHARED_BITMAP_FACTORY create make feature -- Initialization make is -- initialize cutter do end feature -- Factory make_from_image(image_name: STRING; number_of_parts: INTEGER) is -- create number_of_parts of image given by image_name (name of file) require name_valid: image_name /= Void and then not image_name.is_empty local num_p : TUPLE[INTEGER, INTEGER] -- how many parts in width and height part_size: TUPLE[INTEGER, INTEGER] -- size of the parts (width, height) image_size: TUPLE[INTEGER, INTEGER] -- size of the image used (width, height) after cutting orig_width, orig_height : INTEGER -- size of the original image factor : DOUBLE -- the factor the image has to be scaled to fit do last_puzzle := void -- first of all, delete old puzzle bitmap_factory.create_bitmap_from_image(image_name) -- get the picture last_image := bitmap_factory.last_bitmap -- save it locally -- save properties orig_width :=last_image.width orig_height := last_image.height -- now scale image to max_width, max_height factor := scale(orig_width, orig_height) bitmap_factory.create_zoomed_bitmap (last_image, factor) last_image := bitmap_factory.last_bitmap -- go on with calculations on scaled image num_p := calculate_num_p(number_of_parts) -- calculate the number of parts in width and height part_size := calculate_part_size(num_p) -- calculate the size create last_puzzle.make_empty -- initialize result to empty last_puzzle.set_part_size (part_size) -- set parameters we calculated before image_size := [num_p.integer_item (1)*part_size.integer_item (1),num_p.integer_item (2)*part_size.integer_item (2)] last_puzzle.set_image_size (image_size) last_puzzle.set_part_num (num_p) create_parts(num_p, part_size) -- finally create the puzzle ensure puzzle_created: last_puzzle /= Void end feature -- Access last_puzzle : UC_PUZZLE -- the puzzle that was cutted last_image: EM_BITMAP -- and the image, that was used to cut it feature {NONE} -- Implementation calculate_num_p(number_of_parts: INTEGER) : TUPLE[INTEGER, INTEGER] is -- calculate number of parts in row and col require image_not_void: last_image /= void meaningfull_number: number_of_parts > 0 local approx, approx_col, approx_row, ratio : REAL -- a few temporary vars fun : SINGLE_MATH -- need sqrt do create fun -- no comment approx := fun.sqrt(number_of_parts) -- first approximation is the square root ratio := last_image.width / last_image.height -- get the ratio of width and height approx_row := approx * ratio -- now scale approximation by ratio approx_col := approx / ratio -- now do some fancy roundings to get a good result if approx_row.rounded * approx_col.rounded > 0.9* number_of_parts and approx_row.rounded * approx_col.rounded < 1.1* number_of_parts then result := [approx_row.rounded, approx_col.rounded] elseif approx_row.floor * approx_col.ceiling > 0.9* number_of_parts and approx_row.floor * approx_col.ceiling < 1.1* number_of_parts then result := [approx_row.floor, approx_col.floor] elseif approx_row.ceiling * approx_col.floor > 0.9* number_of_parts and approx_row.ceiling * approx_col.floor < 1.1* number_of_parts then result := [approx_row.ceiling, approx_col.floor] else -- lets hope we never get here result := [approx.rounded, approx.rounded] end ensure result_created: result /= Void result_meaningful: result.integer_item (1) > 0 and result.integer_item(2) > 0 result_in_intervall_lower: (result.integer_item (1) * result.integer_item(2)) > 0.9*number_of_parts result_in_intervall_upper: (result.integer_item (1) * result.integer_item(2)) < 1.1*number_of_parts ratio_reflects_image: true -- todo end calculate_part_size(row_col_number: TUPLE[INTEGER, INTEGER]) : TUPLE[INTEGER, INTEGER] is -- calculate size of parts require image_not_void: last_image /= void input_meaningful: row_col_number /= Void and then ( row_col_number.integer_item(1) > 0 and row_col_number.integer_item(2) > 0 ) ratio_reflects_image: true -- todo local p_width, p_height : INTEGER do p_width := last_image.width // row_col_number.integer_item(1) p_height := last_image.height // row_col_number.integer_item(2) result := [p_width, p_height] ensure result_created: result /= Void result_meaningful: result /= Void and then ( result.integer_item(1) > 0 and result.integer_item(2) > 0 ) size_somewhat_quadratic_1: result.integer_item(1) < 2* result.integer_item(2) size_somewhat_quadratic_2: result.integer_item(2) < 2* result.integer_item(1) end create_parts(row_col_number: TUPLE[INTEGER, INTEGER]; size: TUPLE[INTEGER, INTEGER]) is -- does the actual cutting require image_not_void: last_image /= void last_puzzle_not_void: last_puzzle /= void number_useful: row_col_number /= Void and then ( row_col_number.integer_item(1) > 0 and row_col_number.integer_item(2) > 0 ) size_useful: size /= Void and then ( size.integer_item(1) > 0 and size.integer_item(2) > 0 ) local calc_num_of_parts : INTEGER puzz_part: UC_PUZZLE_PART i,j: INTEGER -- counters do calc_num_of_parts := row_col_number.integer_item(1) * row_col_number.integer_item(2) from i := 0 until i >= row_col_number.integer_item(1) loop from j := 0 until j >= row_col_number.integer_item (2) loop bitmap_factory.create_bitmap_from_surface_part ( last_image, i*size.integer_item(1), j*size.integer_item(2), size.integer_item(1), size.integer_item(2)) puzz_part := Void create puzz_part.make_from_bitmap(bitmap_factory.last_bitmap) puzz_part.set_pos (i,j) last_puzzle.extend (puzz_part) j := j+1 end i := i+1 end ensure puzzle_created: last_puzzle /= void end scale(x,y : INTEGER) : DOUBLE is -- calculate zoom-factor such that x*y fits in max_width*max_height do Result := max_width/x Result := Result.min(max_height/y) end feature {NONE} -- constants max_width : INTEGER is 400 max_height : INTEGER is 600 -- this denotes the maximal size, the image should have -- the image is zoomed to fit in this area end -- class QUAD_CUTTER