indexing description: "[ EDITING_PANEL takes a scene and displays it - thats all. User input handling is caught by POLYGON_EDITOR_SCENE and passed to TOOL, which modifies the SCENE. ]" date: "$Date$" revision: "$Revision$" class EDITING_PANEL inherit EM_PANEL redefine draw_body end EM_SHARED_BITMAP_FACTORY export {NONE} all end EM_TIME_SINGLETON export {NONE} all end EM_COLLISION_IMPLEMENTATION export {NONE} all end EM_SHARED_STANDARD_FONTS export {NONE} all end create make feature {NONE} -- Initialisation make (panel_width, panel_height: INTEGER; a_scene: SCENE) is -- Initialise with a drawing panel of size `panel_width' `panel_height'. require size_valid: panel_height > 0 and panel_width > 0 a_scene_void: a_scene /= Void do scene := a_scene make_from_dimension (panel_width, panel_height) set_optimal_dimension (panel_width, panel_height) set_background_color (create {EM_COLOR}.make_white) ensure scene_set: scene = a_scene end feature -- Access background_color: EM_COLOR is -- Background color of panel once create Result.make_white end scene: SCENE -- the current scene, wich is to be displayed selection_border_blink: INTEGER is 500 -- disabled if 0 -- otherwise the delay for blinking in ms blinking_status: BOOLEAN -- blinking status feature -- Element change set_scene (a_scene: SCENE) is -- sets `scene' require a_scene_not_void: a_scene /= Void do scene := a_scene ensure scene_set: scene = a_scene end set_blinking_status (b: BOOLEAN) is -- set `blinking_status' do blinking_status := b ensure blinking_status_set: blinking_status = b end feature -- Drawing draw_body is -- Draw the body of the widget. local bmp: EM_BITMAP str: STRING v1, v2: EM_VECTOR_2D w, h, max_width: INTEGER visible_panes: DS_LINKED_LIST [IMAGE_PANE] circles: DS_LINKED_LIST_CURSOR [CIRCLE] rectangles: DS_LINKED_LIST_CURSOR [RECTANGLE] polygons: DS_LINKED_LIST_CURSOR [POLYGON] vertexes: DS_LINKED_LIST_CURSOR [VERTEX] vectors: DS_LINKED_LIST_CURSOR [EM_VECTOR_2D] a_circle: EM_CIRCLE a_rectangle: EM_RECTANGLE a_polygon: EM_POLYGON splitting_lines_cursor: DS_LINKED_LIST_CURSOR [EM_PAIR [INTEGER, INTEGER]] top_left_x, top_left_y, grid_length, grid_length_scaled, delta, scene_top_left_x, scene_top_left_y, current_grid, first, curr: DOUBLE do Precursor {EM_PANEL} surface.clear surface.fill (background_color) create visible_panes.make scene_top_left_x := scene.center_x - scene.half_screen_width / scene.scale scene_top_left_y := scene.center_y - scene.half_screen_height / scene.scale -- draw all image panes from scene.image_panes.start until scene.image_panes.off loop if scene.scale = 1 and scene.image_panes.item_for_iteration.scale_x = 1 and scene.image_panes.item_for_iteration.scale_y = 1 then -- nothing has to be scaled -> display bmp at the appropriate position top_left_x := scene.image_panes.item_for_iteration.x.rounded - scene.center_x + scene.half_screen_width top_left_y := scene.image_panes.item_for_iteration.y.rounded - scene.center_y + scene.half_screen_height bmp := scene.image_panes.item_for_iteration.image if is_on_panel (top_left_x, top_left_y, top_left_x + bmp.width, top_left_y + bmp.height) then visible_panes.force_last (scene.image_panes.item_for_iteration) bmp.set_alpha_value (scene.image_panes.item_for_iteration.transparency) surface.blit_surface (bmp, top_left_x.rounded, top_left_y.rounded) end else -- draw scaled image v1 := scene.screen_position_from_coordinates (scene.image_panes.item_for_iteration.x, scene.image_panes.item_for_iteration.y) if is_on_panel (v1.x, v1.y, v1.x + scene.image_panes.item_for_iteration.image_scaled.width, v1.y + scene.image_panes.item_for_iteration.image_scaled.height) then visible_panes.force_last (scene.image_panes.item_for_iteration) scene.image_panes.item_for_iteration.image_scaled.set_alpha_value (scene.image_panes.item_for_iteration.transparency) surface.blit_surface (scene.image_panes.item_for_iteration.image_scaled, v1.x.rounded, v1.y.rounded) end end scene.image_panes.forth end -- draw grid surface.set_drawing_color (grid_color) if scene.grid then -- grid length in pixels from grid_length := scene.scale * 100.0 until grid_length <= grid_max_length and grid_length > grid_min_length loop if grid_length > grid_max_length then grid_length := grid_length / 2 else grid_length := grid_length * 2 end end -- `grid_length' is the lenght in pixels on the screen -- `grid_length_scaled' is the actual length of one grid grid_length_scaled := grid_length / scene.scale -- horizontal lines delta := - (scene_top_left_y - ((scene_top_left_y.ceiling // (grid_length_scaled.floor)) * grid_length_scaled)) first := delta * scene.scale curr := ((scene_top_left_y.ceiling // (grid_length_scaled.floor)) * grid_length_scaled) from current_grid := first until current_grid > height loop if current_grid > font_size then str := format_string_00 (curr) w := grid_font.string_width (str) if w > max_width then max_width := w end grid_font.draw_string (str, surface, 0, current_grid.floor - font_size//2) if curr = 0 then surface.put_line_segment (w+2, current_grid.rounded, width, current_grid.rounded, grid_color_darker) else surface.put_line_segment (w+2, current_grid.rounded, width, current_grid.rounded, grid_color) end end current_grid := current_grid + grid_length curr := curr + grid_length_scaled end -- vertical lines delta := - (scene_top_left_x - ((scene_top_left_x.ceiling // (grid_length_scaled.floor)) * grid_length_scaled)) first := delta * scene.scale curr := ((scene_top_left_x.ceiling // (grid_length_scaled.floor)) * grid_length_scaled) from current_grid := first until current_grid > width loop if current_grid > max_width + 2 then str := format_string_00 (curr) grid_font.draw_string (str, surface, current_grid.rounded - grid_font.string_width (str)//2, 0) if curr = 0 then surface.put_line_segment (current_grid.rounded, font_size+1, current_grid.rounded, height, grid_color_darker) else surface.put_line_segment (current_grid.rounded, font_size+1, current_grid.rounded, height, grid_color) end end current_grid := current_grid + grid_length curr := curr + grid_length_scaled end end -- draw circles create circles.make (scene.circles) from circles.start until circles.off loop w := circles.item.resizer.drag_button_size // 2 create a_circle.make (scene.screen_position_from_coordinates (circles.item.center.x, circles.item.center.y), circles.item.radius * scene.scale) if scene.vertex_mode then -- vertex mode a_circle.set_filled (False) a_circle.set_line_width (1) a_circle.set_line_color (object_color_in_vertex_mode) a_circle.draw (surface) -- draw vertexes create vertexes.make (circles.item.vertex_list) from vertexes.start until vertexes.off loop if vertexes.item.is_selected then draw_dragger (vertexes.item.vector, w, object_drag_button_color_selected) else draw_dragger (vertexes.item.vector, w, object_drag_button_color) end vertexes.forth end else a_circle.set_filled (True) if circles.item.is_selected then a_circle.set_fill_color (object_color_in_object_mode_selected) else a_circle.set_fill_color (object_color_in_object_mode) end a_circle.draw (surface) end circles.forth end -- draw rectangles create rectangles.make (scene.rectangles) from rectangles.start until rectangles.off loop w := rectangles.item.top_left.drag_button_size // 2 create a_rectangle.make (rectangles.item.top_left.vector, rectangles.item.bottom_right.vector) if scene.vertex_mode then -- vertex mode a_rectangle.set_filled (False) a_rectangle.set_line_width (1) a_rectangle.set_line_color (object_color_in_vertex_mode) a_rectangle.draw (surface) -- draw vertexes create vertexes.make (rectangles.item.vertex_list) from vertexes.start until vertexes.off loop if vertexes.item.is_selected then draw_dragger (vertexes.item.vector, w, object_drag_button_color_selected) else draw_dragger (vertexes.item.vector, w, object_drag_button_color) end vertexes.forth end else a_rectangle.set_filled (True) if rectangles.item.is_selected then a_rectangle.set_fill_color (object_color_in_object_mode_selected) else a_rectangle.set_fill_color (object_color_in_object_mode) end a_rectangle.draw (surface) end rectangles.forth end -- draw polygons create polygons.make (scene.polygons) from polygons.start until polygons.off loop w := polygons.item.vertex_list.first.drag_button_size // 2 create a_polygon.make_empty create vectors.make (polygons.item) from vectors.start until vectors.off loop a_polygon.extend (scene.screen_position_from_coordinates (vectors.item.x, vectors.item.y)) vectors.forth end -- now check, if the polygon has too few vertices to be drawn as a polygon if a_polygon.count = 2 then -- draw as a line surface.set_drawing_color (object_color_in_vertex_mode) surface.set_line_width (1) surface.draw_line_segment (a_polygon.first, a_polygon.last) if scene.vertex_mode then draw_dragger (a_polygon.first, w, object_drag_button_color) end elseif a_polygon.count >= 3 then -- draw as polygon if scene.vertex_mode then -- draw splitting lines create splitting_lines_cursor.make (polygons.item.splitting_lines) surface.set_drawing_color (splitting_line_color) from splitting_lines_cursor.start until splitting_lines_cursor.off loop v1 := polygons.item.item (splitting_lines_cursor.item.first) v2 := polygons.item.item (splitting_lines_cursor.item.second) surface.draw_line_segment (scene.screen_position_from_coordinates (v1.x, v1.y), scene.screen_position_from_coordinates (v2.x, v2.y)) splitting_lines_cursor.forth end -- drawing settings a_polygon.set_filled (False) if polygons.item.is_collidable_valid then a_polygon.set_line_color (object_color_in_vertex_mode) else a_polygon.set_line_color (object_color_in_vertex_mode_invalid) end a_polygon.set_line_width (1) a_polygon.draw (surface) -- draw vertexes create vertexes.make (polygons.item.vertex_list) from vertexes.start until vertexes.off loop if vertexes.item.is_selected then draw_dragger (vertexes.item.vector, w, object_drag_button_color_selected) else draw_dragger (vertexes.item.vector, w, object_drag_button_color) end vertexes.forth end else a_polygon.set_filled (True) if polygons.item.is_selected then a_polygon.set_fill_color (object_color_in_object_mode_selected) elseif not polygons.item.is_collidable_valid then a_polygon.set_fill_color (object_color_in_object_mode_invalid) else a_polygon.set_fill_color (object_color_in_object_mode) end a_polygon.draw (surface) end end polygons.forth end -- draw selection of image panes from visible_panes.start until visible_panes.off loop if visible_panes.item_for_iteration.is_selected then -- the image pane is selected w := (visible_panes.item_for_iteration.image.width * visible_panes.item_for_iteration.scale_x).rounded h := (visible_panes.item_for_iteration.image.height * visible_panes.item_for_iteration.scale_y).rounded v1 := scene.screen_position_from_coordinates (visible_panes.item_for_iteration.x, visible_panes.item_for_iteration.y) v2 := scene.screen_position_from_coordinates (visible_panes.item_for_iteration.x + w, visible_panes.item_for_iteration.y + h) if selection_border_blink = 0 or not blinking_status then draw_border (v1, v2, selection_color) else draw_border (v1, v2, selection_color_blinked) end end visible_panes.forth end -- draw mouse selection box from SELECTION_TOOL if scene.selection_box_bottom_right /= Void and scene.selection_box_top_left /= Void then surface.put_rectangle (scene.selection_box_top_left.x.rounded, scene.selection_box_top_left.y.rounded, scene.selection_box_bottom_right.x.rounded, scene.selection_box_bottom_right.y.rounded, selection_box_color) end -- draw splitting line which is currently being dragged if (scene.splitting_line.first /= Void and scene.splitting_line.second /= Void) then surface.set_drawing_color (splitting_line_color) surface.draw_line_segment (scene.splitting_line.first, scene.splitting_line.second) end end draw_border (v1, v2: EM_VECTOR_2D; a_color: EM_COLOR) is -- draw border in `a_color'. do surface.put_rectangle_filled (v1.x.rounded - selection_border, v1.y.rounded - selection_border, v2.x.rounded + selection_border - 2, v1.y.rounded - 1, a_color) surface.put_rectangle_filled (v1.x.rounded - selection_border, v2.y.rounded + selection_border - 1, v2.x.rounded + selection_border - 2, v2.y.rounded, a_color) surface.put_rectangle_filled (v1.x.rounded - selection_border, v1.y.rounded, v1.x.rounded - 1, v2.y.rounded - 1, a_color) surface.put_rectangle_filled (v2.x.rounded + selection_border - 2, v1.y.rounded, v2.x.rounded - 1, v2.y.rounded - 1, a_color) end draw_dragger (v: EM_VECTOR_2D; w: INTEGER; a_color: EM_COLOR) is -- draws a dragger do surface.put_rectangle_filled (v.x.rounded - w, v.y.rounded - w, v.x.rounded + 1 + w, v.y.rounded + 1 + w, a_color) end feature {NONE} -- implementation object_color_in_vertex_mode: EM_COLOR is -- object color in vertex mode once create Result.make_with_rgb (0, 93, 255) end object_color_in_vertex_mode_invalid: EM_COLOR is -- object color in object mode once create Result.make_with_rgb (200, 0, 0) end object_color_in_object_mode: EM_COLOR is -- object color in object mode once create Result.make_with_rgb (0, 93, 255) Result.set_alpha (150) end object_color_in_object_mode_invalid: EM_COLOR is -- object color in object mode once create Result.make_with_rgb (200, 0, 0) Result.set_alpha (150) end object_drag_button_color_selected: EM_COLOR is -- object color in vertex mode once create Result.make_with_rgb (0, 255, 0) end object_color_in_object_mode_selected: EM_COLOR is -- object color in object mode once create Result.make_with_rgb (0, 180, 0) Result.set_alpha (100) end object_drag_button_color: EM_COLOR is -- color of the object drag buttons once create Result.make_with_rgb (200, 0, 0) end splitting_line_color: EM_COLOR is -- color of the object drag buttons once create Result.make_with_rgb (0, 255, 0) end grid_color: EM_COLOR is -- grid_color once create Result.make_with_rgb (0, 93, 255) end grid_color_darker: EM_COLOR is -- darker grid_color once create Result.make_with_rgb (0, 20, 160) end selection_box_color: EM_COLOR is -- color of the selection box once create Result.make_with_rgb (0, 255, 0) end selection_color: EM_COLOR is -- color of selection once create Result.make_with_rgb (235, 235, 0) end selection_color_blinked: EM_COLOR is -- color of selection once create Result.make_with_rgb (200, 200, 0) end selection_border: INTEGER is 2 -- size of the selection border grid_min_length: DOUBLE is 50.0 -- minimal length of grid grid_max_length: DOUBLE is 400.0 -- maximal length of grid grid_font: EM_FONT is -- grid font once Result := standard_ttf_fonts.bitstream_vera_sans (font_size) end font_size: INTEGER is 9 -- grid font size panel_collidable: EM_RECTANGLE_COLLIDABLE is -- collidable of the current panel once create Result.make (create {EM_VECTOR_2D}.make (0, 0), create {EM_VECTOR_2D}.make (width, height)) end is_on_panel (a_x, a_y, bottom_right_x, bottom_right_y: DOUBLE): BOOLEAN is -- is the image on the panel? local image_collidable: EM_RECTANGLE_COLLIDABLE do create image_collidable.make (create {EM_VECTOR_2D}.make (a_x, a_y), create {EM_VECTOR_2D}.make (bottom_right_x, bottom_right_y)) Result := intersect_rectangle_rectangle (image_collidable, panel_collidable) end format_string_00 (t: DOUBLE): STRING is -- formats a string from a double, with up to 2 digits after the dot. local str: STRING do str := (t*100).rounded.out if not str.is_equal ("0") then if str.item (str.count).is_equal ('0') then if str.item (str.count - 1).is_equal ('0') then str := str.substring (1, str.count - 2) else str := str.substring (1, str.count) str.insert_character ('.', str.count) end else str.insert_character ('.', str.count - 1) end end Result := str ensure str_not_void: Result /= Void end invariant scene_not_void: scene /= Void end