indexing description: "[ A circle (approximated by a polygon). ]" date: "$Date$" revision: "$Revision$" class EM_CIRCLE inherit EM_CLOSED_FIGURE redefine width, height, set_x_y, update_bounding_box, publish_mouse_event end DOUBLE_MATH export {NONE} all undefine is_equal, copy, default_create end create make, make_inside_box, make_through_three_points feature -- Initialization default_number_of_polygon_points: INTEGER is 60 default_max_number_of_polygon_points: INTEGER is 256 make (a_center: EM_VECTOR_2D; a_radius: DOUBLE) is -- Make circle with `a_center' and `a_radius'. require a_center_not_void: a_center /= Void a_radius_is_positiive: a_radius > 0 do default_create center := a_center radius := a_radius set_visible (True) number_of_polygon_points := default_number_of_polygon_points max_number_of_polygon_points := default_max_number_of_polygon_points build_approximating_polygon update_bounding_box ensure is_visible: is_visible end make_inside_box (a_box: EM_ORTHOGONAL_RECTANGLE) is -- Make largest circle fitting into `a_box' -- with `center' at center of `a_box'. do make (a_box.center, a_box.width.min (a_box.height) / 2) ensure is_visible: is_visible 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 make (create {EM_VECTOR_2D}.make (z1.real, z1.imaginary), z1.to_vector_2d.distance (a_vector)) else -- the 3 points are all in one line. No such circle exists make (create {EM_VECTOR_2D}.make (0,0), 1) not_created := True end ensure is_visible: is_visible end feature -- Status report width: INTEGER is -- Width of `Current'. do Result := (center.x + radius).ceiling - x end height: INTEGER is -- Height of `Current'. do Result := (center.y + radius).ceiling - y end radius: DOUBLE -- Radius of `Current' circle. center: EM_VECTOR_2D -- Center of `Current' circle. number_of_polygon_points: INTEGER -- Number of polygon points used to approximate `Current' circle -- when not `is_approximated_adaptively'. max_number_of_polygon_points: INTEGER -- Maximum number of polygon points used to approximate `Current' circle -- when `is_approximated_adaptively'. min_number_of_polygon_points: INTEGER is 8 -- Minimum number of points used to approximate a circle. is_approximated_adaptively: BOOLEAN -- Is `Current' drawed with an adaptively choosen number of polygon points -- depending on `display_resolution' of surface it is drawed onto? 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 -- Status setting enable_adaptive_approximation is -- Enable drawing `Current' with an adaptively choosen number of polygon points, -- depending on `radius' and surface's `display_resolution'. do is_approximated_adaptively := True end disable_adaptive_approximation is -- Disable drawing `Current' with an adaptively choosen number of polygon points, -- depending on `radius' and surface's `display_resolution'. do is_approximated_adaptively := False end set_center (a_center: EM_VECTOR_2D) is -- Set `center' to `a_center'. do center := a_center build_approximating_polygon update_bounding_box end set_radius (a_radius: DOUBLE) is -- Set `radius' to `a_radius'. do radius := a_radius x := (center.x - radius).floor y := (center.y - radius).floor build_approximating_polygon update_bounding_box end set_number_of_polygon_points (a_count: INTEGER) is -- Set `number_of_polygon_points' to `a_count'. require a_count_big_enough: a_count >= min_number_of_polygon_points do number_of_polygon_points := a_count build_approximating_polygon ensure number_of_polygon_points_set: number_of_polygon_points = a_count end set_max_number_of_polygon_points (a_count: INTEGER) is -- Set `max_number_of_polygon_points' to `a_count'. require a_count_big_enough: a_count >= min_number_of_polygon_points do max_number_of_polygon_points := a_count ensure max_number_of_polygon_points_set: max_number_of_polygon_points = a_count end set_x_y (x_position: INTEGER; y_position: INTEGER) is -- Set `x' and `y' to `x_position' and `y_position'. local diff: EM_VECTOR_2D do create diff.make (x_position - x, y_position - y) center.move_by (diff) y := y_position x := x_position build_approximating_polygon end update_bounding_box is -- Recalculate `bounding_box' (`x', `y', `width', `height') -- to surround `Current'. do x := (center.x - (line_width / 2) - radius).floor y := (center.y - (line_width / 2) - radius).floor internal_width := (center.x + (line_width / 2) + radius).ceiling - x internal_height := (center.y + (line_width / 2) + radius).ceiling - y end feature -- Drawing draw (drawing_interface: EM_SURFACE) is -- Draw `Current' using `drawing_interface'. do if is_visible then if is_approximated_adaptively then number_of_polygon_points := (drawing_interface.device_resolution * radius).floor.max (min_number_of_polygon_points). min(max_number_of_polygon_points) if (number_of_polygon_points - approximating_polygon.count).abs / number_of_polygon_points > 0.25 then build_approximating_polygon end end approximating_polygon.set_fill_color (fill_color) approximating_polygon.set_line_color (line_color) approximating_polygon.set_line_width (line_width) approximating_polygon.set_filled (is_filled) drawing_interface.draw_object (approximating_polygon) end end feature -- Mouse Events publish_mouse_event (a_mouse_event: EM_MOUSE_EVENT) is -- Publish mouse event when `a_mouse_event' occured on `Current'. -- Only publish mouse event, if `proportional_point' lies inside circle. local dist, half_line_width: DOUBLE do dist := a_mouse_event.proportional_position.distance (center) half_line_width := line_width / 2 if dist < radius + half_line_width then if is_filled or else dist > radius - half_line_width then dispatch_mouse_event (a_mouse_event) a_mouse_event.set_caught (True) end end end feature {NONE} -- Implementation approximating_polygon: EM_POLYGON -- Polygon used to approximate `Current' when drawing it. build_approximating_polygon is -- Create polygon points for approximating `Current' circle -- with `number_of_points'. local p1, p2: EM_VECTOR_2D do create p1.make (0, radius) p2 := p1.rotation_around_zero (2 * Pi / number_of_polygon_points) create approximating_polygon.make_regular (number_of_polygon_points, center + p1, p2 - p1) points := approximating_polygon end end