indexing description: "[ Abstraction for drawable figures. Drawable figures have a `line_width' and a `line_color' in which they will be drawed. A figure basicaly is defined through points (`points') so is its position and size (`x', `y', `width', `height'). When the `points' are changed the position and size needs updating (`update_bounding_box'). When the position of a figure is changed (`set_x_y') all its `points' have to be moved accordingly. To ensure updating of the bounding box, descendants have to call `update_bounding_box' whenever something changes that influences the 'bounding_box' of the figure. ]" date: "$Date$" revision: "$Revision$" deferred class EM_FIGURE inherit EM_DRAWABLE redefine default_create, set_x_y end feature {NONE} -- Initialization default_create is -- Initialize with white color and line width 1.0. do Precursor create line_color.make_white line_width := 1.0 create points.make set_visible (True) ensure then is_visible: is_visible end feature -- Status report line_width: DOUBLE -- Line width used to draw `Current' line_color: EM_COLOR -- Line color used to draw `Current' feature -- Queries width: INTEGER is -- Width of `Current' defining its `bounding_box' (`x', `y', `width', `height'). do -- Internaly stored as precalculated. Result := internal_width end height: INTEGER is -- Height of `Current' defining its `bounding_box' (`x', `y', `width', `height'). do -- Internaly stored as precalculated. Result := internal_height end feature -- Status setting set_line_width (a_width: DOUBLE) is -- Set `line_width' to `a_width'. require a_width_not_negative: a_width >= 0 do line_width := a_width update_bounding_box ensure line_width_set: line_width = a_width end set_line_color (a_color: EM_COLOR) is -- Set `line_color' to `a_color'. require a_color_not_void: a_color /= Void do line_color := a_color ensure line_color_assigned: line_color = a_color end set_x_y (x_position: INTEGER; y_position: INTEGER) is -- Set `x' and `y' to `x_position' and `y_position'. -- Moves all contained points accordingly. local cursor: DS_LINKED_LIST_CURSOR [EM_VECTOR_2D] diff: EM_VECTOR_2D do create diff.make (x_position - x, y_position - y) cursor := points.new_cursor from cursor.start until cursor.after loop cursor.item.move_by (diff) cursor.forth end y := y_position x := x_position end update_bounding_box is -- Recalculate `bounding_box' (`x', `y', `width', `height') -- to surround all `points'. local cursor: DS_LINKED_LIST_CURSOR [EM_VECTOR_2D] do if not points.is_empty then x := (points.first.x - (line_width / 2)).floor y := (points.first.y - (line_width / 2)).floor internal_width := (points.first.x + (line_width / 2)).ceiling - x internal_height := (points.first.y + (line_width / 2)).ceiling - y cursor := points.new_cursor from cursor.start until cursor.after loop extend_bounding_box (cursor.item) cursor.forth end else internal_width := 0 internal_height := 0 end end feature {NONE} -- Internal representation points: DS_LINKED_LIST [EM_VECTOR_2D] -- Points that describe `Current'. internal_width: INTEGER -- Internal width of `bounding_box', -- such that descendants can still redefine `width'. internal_height: INTEGER -- Internal height of `bounding_box', -- such that descendants can still redefine `height'. feature {NONE} -- Implementation extend_bounding_box (a_point: EM_VECTOR_2D) is -- Change `Current's `bounding_box' (`x', `y', `width' and `height') -- to contain `a_point'. require a_point_not_void: a_point /= Void local xmin, ymin, xmax, ymax: INTEGER do if points.is_empty or else (points.count = 1 and then points.first = a_point) then -- If there is no other point than `a_point' in `points', -- choose closest bounding box to surround `a_point'. x := (a_point.x - (line_width / 2)).floor y := (a_point.y - (line_width / 2)).floor internal_width := (a_point.x + (line_width / 2)).ceiling - x internal_height := (a_point.y + (line_width / 2)).ceiling - y else -- If there are other points to be surrounded, -- just extend existing `bounding_box'. xmin := (a_point.x - (line_width / 2)).floor ymin := (a_point.y - (line_width / 2)).floor xmax := (a_point.x + (line_width / 2)).ceiling ymax := (a_point.y + (line_width / 2)).ceiling if xmin < x then internal_width := internal_width + x - xmin x := xmin end if ymin < y then internal_height := internal_height + y - ymin y := ymin end if xmax > x + width then internal_width := xmax - x end if ymax > y + height then internal_height := ymax - y end end ensure bounding_box_extended: bounding_box.has (a_point) end invariant line_color_not_void: line_color /= Void line_width_not_negative: line_width >= 0.0 points_not_void: points /= Void end