indexing description: "[ Orthogonal rectangular areas. ]" date: "$Date$" revision: "$Revision$" class EM_ORTHOGONAL_RECTANGLE inherit ANY redefine out end create make, make_from_coordinates, make_from_position_and_size, make_from_rectangle feature -- Initialization make (a_point_a, a_point_b: EM_VECTOR_2D) is -- Create a new rectangle from `a_point_a' and `a_point_b'. require a_point_a_not_void: a_point_a /= Void a_point_b_not_void: a_point_b /= Void do point_a := a_point_a point_b := a_point_b process_changes ensure point_a_set: point_a = a_point_a point_b_set: point_b = a_point_b end make_from_coordinates (x1, y1, x2, y2: DOUBLE) is -- Create a rectangle from DOUBLE coordinates. do make (create {EM_VECTOR_2D}.make (x1,y1), create {EM_VECTOR_2D}.make (x2,y2)) ensure point_a_set: point_a.x = x1 and then point_a.y = y1 point_b_set: point_b.x = x2 and then point_b.y = y2 end make_from_position_and_size (x1, y1, a_width, a_height: DOUBLE) is -- Create a rectangle with upper left corner `x1', `y1' -- and `a_width', `a_height'. require a_width_not_negative: a_width >= 0.0 a_height_not_negative: a_height >= 0.0 do make (create {EM_VECTOR_2D}.make (x1, y1), create {EM_VECTOR_2D}.make (x1 + a_width, y1 + a_height)) ensure upper_left_set: upper_left.x = x1 and then upper_left.y = y1 size_set: width = a_width and then height = a_height end make_from_rectangle (a_rectangle: EM_ORTHOGONAL_RECTANGLE) is -- Initialize from `a_rectangle'. require a_rectangle_not_void: a_rectangle /= Void do make (a_rectangle.point_a.twin, a_rectangle.point_b.twin) ensure initialized_as_a_rectangle: point_a.is_equal (a_rectangle.point_a) and then point_b.is_equal (a_rectangle.point_b) end feature -- Access point_a: EM_VECTOR_2D -- One corner point of the rectangle point_b: EM_VECTOR_2D -- The other corner point of the rectangle feature -- Status report width: DOUBLE is -- Width of `Current' do Result := (point_a.x - point_b.x).abs end height: DOUBLE is -- Height of `Current' do Result := (point_a.y - point_b.y).abs end size: DOUBLE is -- Size of `Current'. do Result := width * height ensure size_calculated: size > width * height - eps and size < width * height + eps end feature -- Element change right_by (a_distance: DOUBLE) is -- Move `Current' right by `a_distance'. do point_a.right_by (a_distance) point_b.right_by (a_distance) process_changes ensure left_bound_moved_right: left_bound > old left_bound + a_distance - eps and left_bound < old left_bound + a_distance + eps right_bound_moved_right: right_bound > old right_bound + a_distance - eps and right_bound < old right_bound + a_distance + eps end up_by (a_distance: DOUBLE) is -- Move `Current' up by `a_distance'. do point_a.up_by (a_distance) point_b.up_by (a_distance) process_changes ensure upper_bound_moved_up: upper_bound > old upper_bound - a_distance - eps and upper_bound < old upper_bound - a_distance + eps lower_bound_moved_up: lower_bound > old lower_bound - a_distance - eps and lower_bound < old lower_bound - a_distance + eps end down_by (a_distance: DOUBLE) is -- Move `Current' down by `a_distance'. do point_a.down_by (a_distance) point_b.down_by (a_distance) process_changes ensure upper_bound_moved_down: upper_bound > old upper_bound + a_distance - eps and upper_bound < old upper_bound + a_distance + eps lower_bound_moved_down: lower_bound > old lower_bound + a_distance - eps and lower_bound < old lower_bound + a_distance + eps end left_by (a_distance: DOUBLE) is -- Move `Current' left by `a_distance'. do point_a.left_by (a_distance) point_b.left_by (a_distance) process_changes ensure left_bound_moved_left: left_bound > old left_bound - a_distance - eps and left_bound < old left_bound - a_distance + eps right_bound_moved_left: right_bound > old right_bound - a_distance - eps and right_bound < old right_bound - a_distance + eps end move_by (a_distance: EM_VECTOR_2D) is -- Move `Current' by the vector `a_distance'. require a_distance_not_void: a_distance /= Void a_distance_not_point_a: a_distance /= point_a a_distance_not_point_b: a_distance /= point_b do point_a.move_by (a_distance) point_b.move_by (a_distance) process_changes ensure left_bound_moved: left_bound > old left_bound + a_distance.x - eps and left_bound < old left_bound + a_distance.x + eps right_bound_moved: right_bound > old right_bound + a_distance.x - eps and right_bound < old right_bound + a_distance.x + eps upper_bound_moved: upper_bound > old upper_bound + a_distance.y - eps and upper_bound < old upper_bound + a_distance.y + eps lower_bound_moved: lower_bound > old lower_bound + a_distance.y - eps and lower_bound < old lower_bound + a_distance.y + eps end move_to (a_position: EM_VECTOR_2D) is -- Move `upper_left' to `a_position'. require a_position_not_void: a_position /= Void local a_distance: EM_VECTOR_2D do create a_distance.make_distance (upper_left, a_position) point_a.move_by (a_distance) point_b.move_by (a_distance) process_changes ensure -- upper_left_moved: upper_left.is_equal (a_position) size_unchanged: size = old size end move_center_to (a_position: EM_VECTOR_2D) is -- Move `center' to `a_position'. require a_position_not_void: a_position /= Void local a_distance: EM_VECTOR_2D do create a_distance.make_distance (center, a_position) point_a.move_by (a_distance) point_b.move_by (a_distance) process_changes ensure -- center_moved: center.is_equal (a_position) size_unchanged: size = old size end scale (a_factor: DOUBLE) is -- Scalar multiplication by `a_factor' do point_a.scale (a_factor) point_b.scale (a_factor) process_changes end zoom (a_factor: DOUBLE) is -- Zoom `Current' by `a_factor' with respect to `center'. local dist, c: EM_VECTOR_2D do c := center dist := point_a - c dist.scale (a_factor) point_a := c + dist point_b := c - dist process_changes end extend (a_point: EM_VECTOR_2D) is -- Resize `Current' to contain `a_point'. require a_point_attached: a_point /= Void local min_x, min_y, max_x, max_y: DOUBLE do min_x := left_bound.min (a_point.x) min_y := upper_bound.min (a_point.y) max_x := right_bound.max (a_point.x) max_y := lower_bound.max (a_point.y) point_a.make (min_x, min_y) point_b.make (max_x, max_y) process_changes ensure has_a_point: has (a_point) end set_size (a_width, a_height: DOUBLE) is -- Set size to `a_width' and `a_height' -- by not changing `upper_left' require a_width_not_negative: a_width >= 0 a_height_not_negative: a_height >= 0 local min_x, min_y: DOUBLE do min_x := left_bound min_y := upper_bound point_a.make (min_x, min_y) point_b.make (min_x + a_width, min_y + a_height) process_changes ensure -- width_set: width = a_width -- height_set: height = a_height upper_left_kept: upper_left.is_equal (old upper_left) end set_size_centered (a_width, a_height: DOUBLE) is -- Set size to `a_width' and `a_height' -- by not changing `center' require a_width_not_negative: a_width >= 0 a_height_not_negative: a_height >= 0 local diff_x, diff_y: DOUBLE do diff_x := a_width / 2 diff_y := a_height / 2 point_a.make (center.x - diff_x, center.y - diff_y) point_b.make (point_a.x + a_width, point_a.y + a_height) process_changes end feature -- Queries upper_left: EM_VECTOR_2D is -- Upper-left corner of `Current' do create Result.make (point_a.x.min (point_b.x), point_a.y.min (point_b.y)) ensure result_not_void: Result /= Void end upper_right: EM_VECTOR_2D is -- Upper-right corner of `Current' do create Result.make (point_a.x.max (point_b.x), point_a.y.min (point_b.y)) ensure result_not_void: Result /= Void end lower_left: EM_VECTOR_2D is -- Lower-left corner of `Current' do create Result.make (point_a.x.min (point_b.x), point_a.y.max (point_b.y)) ensure result_not_void: Result /= Void end lower_right: EM_VECTOR_2D is -- Lower-right corner of `Current' do create Result.make (point_a.x.max (point_b.x), point_a.y.max (point_b.y)) ensure result_not_void: Result /= Void end center: EM_VECTOR_2D is -- Center of `Current' do create Result.make ((point_a.x+point_b.x) / 2, (point_a.y+point_b.y) / 2) ensure result_not_void: Result /= Void end upper_bound: DOUBLE is -- Upper bound of `Current' do Result := point_a.y.min (point_b.y) end lower_bound: DOUBLE is -- Upper bound of `Current' do Result := point_a.y.max (point_b.y) end left_bound: DOUBLE is -- Upper bound of `Current' do Result := point_a.x.min (point_b.x) end right_bound: DOUBLE is -- Upper bound of `Current' do Result := point_a.x.max (point_b.x) end has (a_coordinate: EM_VECTOR_2D): BOOLEAN is -- Is `a_coordinate' inside `Current'? require a_coordinate_not_void: a_coordinate /= Void do Result := (a_coordinate.x >= left_bound) and (a_coordinate.y >= upper_bound) and (a_coordinate.x <= right_bound) and (a_coordinate.y <= lower_bound) end intersects (other: like Current): BOOLEAN is -- Do `Current' and `other' overlap? require other_not_void: other /= Void do Result := not ( (right_bound < other.left_bound) or (left_bound > other.right_bound) or (upper_bound > other.lower_bound) or (lower_bound < other.upper_bound)) end intersection (other: like Current): like Current is -- Overlaping area of `Current' with `other'. require other_not_void: other /= Void local left, right, upper, lower: DOUBLE do left := left_bound.max (other.left_bound) right := right_bound.min (other.right_bound) upper := upper_bound.max (other.upper_bound) lower := lower_bound.min (other.lower_bound) create Result.make_from_coordinates (left, upper, right.max (left), lower.max (upper)) ensure zero_sized_result_when_not_overlaping: not intersects (other) implies Result.size = 0 end feature -- Change Processing process_changes is -- Process changes of `Current' rectangle state. -- (Can be redefined by descendants to do something -- whenever the state of `Current' rectangle changes) do end feature -- Output out: STRING is -- Return readable string. do Result := "(X1: " + upper_left.x.out + ", Y1: " + upper_left.y.out + ", X2: " + lower_right.x.out +", Y2: " + lower_right.y.out + ")" end feature -- Constants eps: DOUBLE is 0.01 -- Epsilon for assertions with DOUBLES invariant width_not_negative: width >= 0 height_not_negative: height >= 0 point_a_not_void: point_a /= Void point_b_not_void: point_b /= Void end