indexing description: "[ A composition of collidables, may be used to create concave collidables. ]" date: "$Date$" revision: "$Revision$" class EM_COLLIDABLE_COMPOSITION inherit EM_COLLIDABLE redefine collides_with, process_changes, move_by, set_center end create make, make_from_other feature -- Initialization make (a_collidable: EM_COLLIDABLE; a_center: EM_VECTOR_2D) is -- creates a collidable composition, with the center at `a_center' require a_collidable_not_void: a_collidable /= Void do create center.make (a_center.x, a_center.y) create collidables.make collidables.force_last (a_collidable) initialize process_changes ensure center_set: center.x = a_center.x and center.y = a_center.y end make_from_other (a_collidable: EM_COLLIDABLE_COMPOSITION) is -- copies a collidable local cursor: DS_LINKED_LIST_CURSOR [EM_COLLIDABLE] do create center.make (a_collidable.center.y, a_collidable.center.y) create collidables.make create cursor.make (a_collidable.collidables) from cursor.start until cursor.off loop collidables.force_last (cursor.item.duplicate) cursor.forth end initialize process_changes 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_composition_circle (current, a_circle) elseif a_rectangle /= Void then -- check for collision: composition with rectangle collision := intersect_composition_rectangle (current, a_rectangle) elseif a_composition /= Void then -- check for collision: composition with composition collision := intersect_composition_composition (current, a_composition) elseif a_polygonconvex /= Void then -- check for collision: composition with polygon collision := intersect_composition_polygonconvex (current, a_polygonconvex) else debug logger.write_warning ("A composition 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.extend_last (intersection_point_composition_circle (current, a_circle)) elseif a_rectangle /= Void then -- check for collision: composition with rectangle list.extend_last (intersection_point_composition_rectangle (current, a_rectangle)) elseif a_polygonconvex /= Void then -- check for collision: composition with polygonconvex list.extend_last (intersection_point_composition_polygonconvex (current, a_polygonconvex)) elseif a_composition /= Void then -- check for collision: composition with composition list.extend_last (intersection_point_composition_composition (current, a_composition)) else debug logger.write_warning ("A circle 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 count: INTEGER is -- number of elements do Result := collidables.count end feature -- Element change set_rotatable (v: BOOLEAN) is -- sets `is_rotatable' of each element. local cursor: DS_LINKED_LIST_CURSOR [EM_COLLIDABLE] do rotatable := v create cursor.make (collidables) from cursor.start until cursor.off loop cursor.item.set_rotatable (v) cursor.forth end end add (a_collidable: EM_COLLIDABLE) is -- adds a collidable to the composition. require a_collidable_not_void: a_collidable /= Void do collidables.force_last (a_collidable) process_changes ensure added: old collidables.count = collidables.count - 1 end remove (a_collidable: EM_COLLIDABLE) is -- removes a collidable from the composition. require a_collidable_not_void: a_collidable /= Void do collidables.delete (a_collidable) process_changes end wipe_out is -- empties the list do collidables.wipe_out end set_center (a_center: EM_VECTOR_2D) is -- sets `center' and moves all collidables accordingly do move_by (a_center.x - last_center.x, a_center.y - last_center.y) last_center.set_x (center.x) last_center.set_y (center.y) center := create {EM_VECTOR_2D}.make_from_other (a_center) end set_center_only (a_center: EM_VECTOR_2D) is -- sets `center' only do last_center.set_x (center.x) last_center.set_y (center.y) center := create {EM_VECTOR_2D}.make_from_other (a_center) end move_by (an_x, a_y: DOUBLE) is -- moves the center by an_x, a_y. local cursor: DS_LINKED_LIST_CURSOR [EM_COLLIDABLE] do -- move center last_center.set_x (center.x) last_center.set_y (center.y) center.set_x (center.x + an_x) center.set_y (center.y + a_y) -- move all collidables create cursor.make (collidables) from cursor.start until cursor.off loop cursor.item.move_by (an_x, a_y) cursor.forth end end feature -- Access process_changes is -- processes changes of the collidable do radius := max_distance_to_position (center) end collidables: DS_LINKED_LIST [EM_COLLIDABLE] -- list of contained collidables 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 a_circle: EM_CIRCLE cursor: DS_LINKED_LIST_CURSOR [EM_COLLIDABLE] do create cursor.make (collidables) from cursor.start until cursor.off loop cursor.item.set_draw_clipping (clipping, zoom_factor) if filled then cursor.item.set_draw_filled (True) else cursor.item.set_draw_filled (False) end cursor.item.set_draw_color (color) cursor.item.draw (a_surface) cursor.forth end 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) local cursor: DS_LINKED_LIST_CURSOR [EM_COLLIDABLE] cent: EM_VECTOR_2D a: DOUBLE do create cursor.make (collidables) -- accumulated weighted centers create cent.make (0, 0) -- total area a := 0 from cursor.start until cursor.off loop cent := cent + cursor.item.center_of_mass * cursor.item.area a := a + cursor.item.area cursor.forth end if a > 0 then Result := create {EM_VECTOR_2D}.make_from_other (cent/a) else Result := create {EM_VECTOR_2D}.make_from_other (cent) end end area: DOUBLE is -- area of the collidable in pixel^2 local cursor: DS_LINKED_LIST_CURSOR [EM_COLLIDABLE] a: DOUBLE do create cursor.make (collidables) from cursor.start until cursor.off loop a := a + cursor.item.area cursor.forth end Result := a 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 cursor: DS_LINKED_LIST_CURSOR [EM_COLLIDABLE] do create cursor.make (collidables) from cursor.start until cursor.off loop dist := cursor.item.max_distance_to_position (a_position) if dist > Result then Result := dist end cursor.forth end end duplicate: like Current is -- returns an equivalent copy of the collidable (just the position and borders) do Result := duplicate_composition end duplicate_composition: like Current is -- returns an equivalent copy of the collidable (just the position and borders) do create Result.make_from_other (current) end feature {NONE} -- Implementation turn_around_center (an_angle: DOUBLE; a_center: EM_VECTOR_2D) is -- rotate around center by a certain angle local cursor: DS_LINKED_LIST_CURSOR [EM_COLLIDABLE] do create cursor.make (collidables) from cursor.start until cursor.off loop cursor.item.rotate_by_around_center (an_angle, a_center) cursor.forth end center.make_from_other (center.rotation (a_center, an_angle)) end end