indexing description: "[ A circular bounding area for the collision detection. ]" date: "$Date$" revision: "$Revision$" class EM_CIRCLE_COLLIDABLE inherit EM_COLLIDABLE redefine collides_with end create make, make_from_other, make_from_circle, make_through_three_points feature -- Initialization make (a_center: EM_VECTOR_2D; a_radius: DOUBLE) is -- creates a collidable circle -- to create a collidable dot, just set radius = 0 require a_center_not_void: a_center /= Void a_radius_non_negative: a_radius >= 0 do center := create {EM_VECTOR_2D}.make_from_other (a_center) last_center := create {EM_VECTOR_2D}.make_from_other (center) radius := a_radius rotatable := true initialize ensure radius_set: radius = a_radius center_set: center.x = a_center.x and center.y = a_center.y end make_from_other (a_circle: EM_CIRCLE_COLLIDABLE) is -- creates a collidable circle which is equivalent to the given argument do create center.make (a_circle.center.x, a_circle.center.y) last_center := create {EM_VECTOR_2D}.make_from_other (center) radius := a_circle.radius rotatable := true initialize ensure then radius_set: radius = a_circle.radius center_set: center.x = a_circle.center.x and center.y = a_circle.center.y end make_from_circle (a_circle: EM_CIRCLE) is -- creates a collidable circle which is equivalent to the given argument require a_circle_not_void: a_circle /= Void do create center.make (a_circle.x, a_circle.y) last_center := create {EM_VECTOR_2D}.make_from_other (center) radius := a_circle.radius rotatable := true initialize ensure radius_set: radius = a_circle.radius center_set: center.x = a_circle.x and center.y = a_circle.y end make_through_three_points (a_vector, b_vector, c_vector: EM_VECTOR_2D) is -- creates a circle, that goes through the 3 given points -- if the 3 points are all aligned no such circle can be found and the unit circle is created -- in that case the variable `not_created' will be set, to inform the client require a_vector_not_void: a_vector /= Void b_vector_not_void: b_vector /= Void c_vector_not_void: c_vector /= Void local z1, z2, z3, dividor: EM_COMPLEX do create z1.make (a_vector.x, a_vector.y) create z2.make (b_vector.x, b_vector.y) create z3.make (c_vector.x, c_vector.y) dividor := z1 * (z3-z2).conjugated + z2 * (z1-z3).conjugated + z3 * (z2-z1).conjugated if dividor.real /= 0 or dividor.imaginary /= 0 then z1 := ((z2-z3).times (z1.abs_squared) + (z3-z1).times (z2.abs_squared) + (z1-z2).times (z3.abs_squared)) / dividor center := z1.to_vector_2d last_center := create {EM_VECTOR_2D}.make_from_other (center) radius := center.distance (a_vector) else -- the 3 points are all in one line. No such circle exists not_created := True create center.make (0, 0) radius := 1 end rotatable := true initialize 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 := True elseif a_rectangle /= Void then -- check for collision: circle with rectangle collision := intersect_circle_rectangle (current, a_rectangle) elseif a_composition /= Void then -- check for collision: polygon with polygon collision := intersect_composition_circle (a_composition, current) elseif a_polygonconvex /= Void then -- check for collision: polygon with polygon collision := intersect_polygonconvex_circle (a_polygonconvex, current) else debug logger.write_warning ("A circle 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_circle (a_circle, current)) elseif a_rectangle /= Void then -- check for collision: circle with rectangle list.force_last (intersection_point_circle_rectangle (current, a_rectangle)) elseif a_polygonconvex /= Void then -- check for collision: circle with polygonconvex list.force_last (intersection_point_polygonconvex_circle (a_polygonconvex, current)) elseif a_composition /= Void then -- check for collision: circle with polygon list.extend_last (intersection_point_composition_circle (a_composition, current)) 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 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 do Result := a_position.distance (center) + radius end feature -- Element change set_rotatable (v: BOOLEAN) is -- sets if the do rotatable := v end set_radius (v: DOUBLE) is -- sets `radius' do radius := v ensure radius_set: radius = v end feature -- Access em_circle: EM_CIRCLE is -- returns an equivalent EM_CIRCLE object do Result := create {EM_CIRCLE}.make (center, radius) end not_created: BOOLEAN -- will be set to true, if the circle could not be created correctly (in case of 3 aligned points in `make_through_three_points') 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 do create a_circle.make ((center - clipping) * zoom_factor, radius * zoom_factor) if filled then a_circle.set_fill_color (color) else a_circle.set_filled (false) a_circle.set_line_color (color) a_circle.set_line_width (1) end a_circle.draw (a_surface) 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 (center.x, center.y) end area: DOUBLE is -- area of the collidable in pixel^2 do Result := radius * pi end duplicate: like Current is -- returns an equivalent copy of the collidable (just the position and borders) do Result := duplicate_circle end duplicate_circle: EM_CIRCLE_COLLIDABLE is -- returns an equivalent copy of the object as a EM_CIRCLE_COLLIDABLE do create Result.make (center, radius) end feature {NONE} -- Implementation turn_around_center (an_angle: DOUBLE; a_center: EM_VECTOR_2D) is -- nothing has to be done local center2: EM_VECTOR_2D do if a_center.x /= center.x or a_center.y /= center.y then -- the rotation point is not in the center center2 := center.rotation (a_center, normalized_angle (an_angle)) center.set_x (center2.x) center.set_y (center2.y) angle := an_angle end end end