indexing description: "[ A rectangular (orthogonal) bounding area for the collision detection. This may not be rotated! ]" date: "$Date$" revision: "$Revision$" class EM_RECTANGLE_COLLIDABLE inherit EM_COLLIDABLE redefine collides_with, process_changes end create make, make_from_position_and_size, make_from_other, make_from_rectangle feature -- Creation make (a_corner, b_corner: EM_VECTOR_2D) is -- creates a rectangle from 2 corners require a_corner_not_void: a_corner /= Void b_corner_not_void: b_corner /= Void do center := (a_corner + b_corner) / 2 last_center := create {EM_VECTOR_2D}.make_from_other (center) half_width := (a_corner.x - b_corner.x).abs / 2 half_height := (a_corner.y - b_corner.y).abs / 2 radius := sqrt (half_width * half_width + half_height * half_height) initialize end make_from_position_and_size (a_center: EM_VECTOR_2D; a_width, a_height: DOUBLE) is -- creates a rectangle from the center and the rectangle width/height require a_center_not_void: a_center /= Void do center := a_center last_center := create {EM_VECTOR_2D}.make_from_other (center) half_width := a_width / 2 half_height := a_height / 2 radius := sqrt (half_width * half_width + half_height * half_height) initialize end make_from_other (other: EM_RECTANGLE_COLLIDABLE) is -- creates a rectangle from another EM_RECTANGLE_COLLIDABLE object do center := create {EM_VECTOR_2D}.make_from_other (other.center) last_center := create {EM_VECTOR_2D}.make_from_other (center) half_width := other.half_width half_height := other.half_height radius := sqrt (half_width * half_width + half_height * half_height) initialize end make_from_rectangle (a_rectangle: EM_RECTANGLE) is -- creates a rectangle from another EM_RECTANGLE object require a_rectangle_not_void: a_rectangle /= Void do center := create {EM_VECTOR_2D}.make_from_other (a_rectangle.center) last_center := create {EM_VECTOR_2D}.make_from_other (center) half_width := a_rectangle.width / 2 half_height := a_rectangle.height / 2 initialize process_changes end feature -- Access width: DOUBLE is -- width of the rectangle do Result := half_width * 2 end height: DOUBLE is -- height of the rectangle do Result := half_height * 2 end half_width: DOUBLE -- half of the width half_height: DOUBLE -- half of the height corner_top_left: EM_VECTOR_2D is -- the absolute position of the top left corner of the rectangle do Result := create {EM_VECTOR_2D}.make (center.x - half_width, center.y - half_height) end corner_bottom_left: EM_VECTOR_2D is -- the absolute position of the bottom left corner of the rectangle do Result := create {EM_VECTOR_2D}.make (center.x - half_width, center.y + half_height) end corner_top_right: EM_VECTOR_2D is -- the absolute position of the top right corner of the rectangle do Result := create {EM_VECTOR_2D}.make (center.x + half_width, center.y - half_height) end corner_bottom_right: EM_VECTOR_2D is -- the absolute position of the bottom right corner of the rectangle do Result := create {EM_VECTOR_2D}.make (center.x + half_width, center.y + half_height) end border_top: DOUBLE is -- y value of the top border do Result := center.y - half_height end border_bottom: DOUBLE is -- y value of the bottom border do Result := center.y + half_height end border_left: DOUBLE is -- x value of the left border do Result := center.x - half_width end border_right: DOUBLE is -- x value of the left border do Result := center.x + half_width end em_rectangle: EM_RECTANGLE is -- returns an equivalent EM_RECTANGLE object do Result := create {EM_RECTANGLE}.make (corner_bottom_left, corner_bottom_right) end em_polygon_convex_collidable: EM_POLYGON_CONVEX_COLLIDABLE is -- returns an equivalent EM_POLYGON_CONVEX_COLLIDABLE object local list: DS_LINKED_LIST [EM_VECTOR_2D] do create list.make list.force_last (corner_top_left) list.force_last (corner_top_right) list.force_last (corner_bottom_right) list.force_last (corner_bottom_left) create Result.make_from_absolute_list (center, list) end vertexes: DS_LINKED_LIST [EM_VECTOR_2D] is -- ist of al vertexes local list: DS_LINKED_LIST [EM_VECTOR_2D] do create list.make list.force_last (corner_top_left) list.force_last (corner_top_right) list.force_last (corner_bottom_right) list.force_last (corner_bottom_left) create Result.make_from_linear (list) end feature -- Status Report collides_with (a_collidable: EM_COLLIDABLE): BOOLEAN is -- checks if two objects collide local collision: BOOLEAN dx, dy: DOUBLE a_circle: EM_CIRCLE_COLLIDABLE a_rectangle: EM_RECTANGLE_COLLIDABLE a_composition: EM_COLLIDABLE_COMPOSITION a_polygonconvex: EM_POLYGON_CONVEX_COLLIDABLE do dx := a_collidable.center.x - center.x dy := a_collidable.center.y - center.y if dx*dx + dy*dy <= (radius + a_collidable.radius)*(radius + a_collidable.radius) then -- a rough collision was detected -- -> check for exact collision a_circle ?= a_collidable a_rectangle ?= a_collidable a_composition ?= a_collidable a_polygonconvex ?= a_collidable if a_circle /= Void then -- the other collidable was a circle collision := intersect_circle_rectangle (a_circle, current) elseif a_rectangle /= Void then -- check for collision: circle with rectangle collision := intersect_rectangle_rectangle (current, a_rectangle) elseif a_composition /= Void then -- check for collision: polygon with polygon collision := intersect_composition_rectangle (a_composition, current) elseif a_polygonconvex /= Void then -- check for collision: polygon with polygon collision := intersect_polygonconvex_rectangle (a_polygonconvex, current) else debug logger.write_warning ("A rectangle collided with an unknown collidable, please check if the functions `collides_with' have been redefined correctly.") end end end Result := collision end intersection_points (a_collidable: EM_COLLIDABLE): DS_LINKED_LIST [EM_PAIR [EM_VECTOR_2D, EM_DIRECTION_2D]] is -- returns the average collision point and tangential direction of the 2 collidable objects `current' and `a_collidable' -- if no intersection points were found, returns `Void' local a_circle: EM_CIRCLE_COLLIDABLE a_rectangle: EM_RECTANGLE_COLLIDABLE a_composition: EM_COLLIDABLE_COMPOSITION a_polygonconvex: EM_POLYGON_CONVEX_COLLIDABLE list: DS_LINKED_LIST [EM_PAIR [EM_VECTOR_2D, EM_DIRECTION_2D]] do create list.make a_circle ?= a_collidable a_rectangle ?= a_collidable a_composition ?= a_collidable a_polygonconvex ?= a_collidable if a_circle /= Void then -- the other collidable was a circle list.force_last (intersection_point_circle_rectangle (a_circle, current)) elseif a_rectangle /= Void then -- check for collision: circle with rectangle list.force_last (intersection_point_rectangle_rectangle (a_rectangle, current)) elseif a_polygonconvex /= Void then -- check for collision: rectangle with polygonconvex list.force_last (intersection_point_polygonconvex_rectangle (a_polygonconvex, current)) elseif a_composition /= Void then -- check for collision: rectangle with polygon list.extend_last (intersection_point_composition_rectangle (a_composition, current)) else debug logger.write_warning ("A rectangle was checked for a collision with an unknown collidable, ") logger.write_warning ("please check if the functions `intersection_point' have been redefined correctly.") end end Result := list end feature -- Element change process_changes is -- Processes all changes do -- recompute radius radius := sqrt (half_width * half_width + half_height * half_height) end set_rotatable (v: BOOLEAN) is -- an orthogonal rectangle can not be rotated! do rotatable := false debug logger.write_warning ("An orthogonal rectangle is not rotatable.") end end set_width (v: DOUBLE) is -- sets the width of the rectangle do half_width := v / 2 process_changes ensure half_width_set: half_width = v / 2 end set_height (v: DOUBLE) is -- sets the height of the rectangle do half_height := v / 2 process_changes ensure half_height_set: half_height = v / 2 end feature {EM_COLLISION_DETECTOR} -- Drawing draw (a_surface: EM_SURFACE) is -- Draw `Current' to `a_surface' -- The screen clipping is set with `set_draw_clipping' local rect: EM_RECTANGLE a_center: EM_VECTOR_2D a_diag: EM_VECTOR_2D a_circle: EM_CIRCLE do create a_center.make_from_other ((center - clipping) * zoom_factor) create a_diag.make (half_width, half_height) create rect.make (a_center - a_diag * zoom_factor, a_center + a_diag * zoom_factor) if filled then rect.set_fill_color (color) else rect.set_filled (false) rect.set_line_color (color) rect.set_line_width (1) end rect.draw (a_surface) if draw_circumcircle then create a_circle.make ((center - clipping) * zoom_factor, radius * zoom_factor) a_circle.set_filled (False) a_circle.set_line_width (1) a_circle.set_line_color (draw_circumcircle_color) a_circle.draw (a_surface) end end feature -- Computation center_of_mass: EM_VECTOR_2D is -- returns the center of mass (also known as the centroid) do Result := create {EM_VECTOR_2D}.make_from_other (center) end area: DOUBLE is -- area of the collidable in pixel^2 do Result := width * height end duplicate: like Current is -- returns an equivalent copy of the collidable (just the position and borders) do Result := duplicate_rectangle end duplicate_rectangle: EM_RECTANGLE_COLLIDABLE is -- returns an equivalent copy of the object as a EM_RECTANGLE_COLLIDABLE do create Result.make (corner_top_left, corner_bottom_right) end feature {NONE} -- Implementation turn_around_center (an_angle: DOUBLE; a_center: EM_VECTOR_2D) is -- orthogonal rectangles are not rotatable do debug logger.write_warning ("An orthogonal rectangle is not rotatable.") end end max_distance_to_position (a_position: EM_VECTOR_2D): DOUBLE is -- computes the maximal distance to a certain position. -- this is used to compute the radius of compositions of collidables local dist: DOUBLE do from vertexes.start until vertexes.off loop dist := vertexes.item_for_iteration.distance (a_position) if dist > Result then Result := dist end vertexes.forth end end invariant width_positive: half_width >= 0 height_positive: half_height >= 0 end