note description: "Curves as defined by two points, a `start_angle' and%N% %`aperture'." legal: "See notice at end of class." status: "See notice at end of class." keywords: "figure, arc, curve" date: "$Date$" revision: "$Revision$" class EV_FIGURE_ARC obsolete "Use EV_MODEL_ARC instead. [2017-05-31]" inherit EV_ATOMIC_FIGURE export {ANY} Pi redefine default_create, bounding_box end EV_DOUBLE_POINTED_FIGURE undefine default_create end create default_create, make_with_points feature {NONE} -- Initialization default_create -- Create with some `start_angle' and `aperture'. do Precursor {EV_ATOMIC_FIGURE} start_angle := 0.2 aperture := Pi end feature -- Access start_angle: DOUBLE -- Angle that defines start of arc. aperture: DOUBLE -- Angle that defines percentage of arc. feature -- Status setting set_start_angle (a_start_angle: DOUBLE) -- Set `start_angle' to `a_start_angle'. require a_start_angle_within_bounds: a_start_angle >= 0 and a_start_angle <= 2 * Pi do start_angle := a_start_angle ensure start_angle_assigned: start_angle = a_start_angle end set_aperture (an_aperture: DOUBLE) -- Set `aperture' to `an_aperture'. require an_aperture_within_bounds: an_aperture >= 0 and an_aperture <= 2 * Pi do aperture := an_aperture ensure aperture_assigned: aperture = an_aperture end feature -- Events bounding_box: EV_RECTANGLE -- Smallest orthogonal rectangular area `Current' fits in. local theta_start, theta_end: DOUBLE leftmost, rightmost, topmost, bottommost: INTEGER a, b, center_x, center_y: INTEGER top, left, bottom, right: INTEGER pax, pay, pbx, pby: INTEGER end_angle: DOUBLE cosine_theta_start, cosine_theta_end, sine_theta_start, sine_theta_end: DOUBLE do pax := point_a.x_abs pay := point_a.y_abs pbx := point_b.x_abs pby := point_b.y_abs end_angle := start_angle + aperture top := pay.min (pby) left := pax.min (pbx) bottom := pby.max (pay) right := pbx.max (pax) center_x := (left + right) // 2 center_y := (top + bottom) // 2 a := (right - left) // 2 b := (bottom - top) // 2 -- positive downwards -- The calculations for the bounding box start here -- Map the image angles to the parametric angles, correcting for the tangent theta_start := arc_tangent (a / b * tangent(start_angle)) if start_angle > pi_half and start_angle < pi_half_times_three then theta_start := theta_start + Pi end theta_end := arc_tangent(a / b * tangent(end_angle)) if modulo(end_angle, pi_times_two) > pi_half and modulo(end_angle, pi_times_two) < pi_half_times_three then theta_end := theta_end + Pi end -- Find the bounding box for the three points on the pie -- Watch the signs because conventions are not completely observed cosine_theta_start := cosine(theta_start) cosine_theta_end := cosine(theta_end) sine_theta_start := sine(theta_start) sine_theta_end := sine(theta_end) leftmost := (center_x + a * cosine_theta_start.min(cosine_theta_end)).floor rightmost := (center_x + a * cosine_theta_start.max(cosine_theta_end)).ceiling topmost := (center_y - b * sine_theta_start.max(sine_theta_end)).floor bottommost := (center_y - b * sine_theta_start.min(sine_theta_end)).ceiling -- Adjust for extreme excursions of the pie, when the box is tangent to the curve -- Assumes positve values for aperture and start_angle if (start_angle < pi_half and start_angle + aperture > pi_half) or (start_angle > pi_half and start_angle + aperture > pi_half_times_three + pi) then topmost := top end if (start_angle < Pi and start_angle + aperture > Pi) or (start_angle > pi and start_angle + aperture > pi_times_three) then leftmost := left end if (start_angle < pi_half_times_three and start_angle + aperture > pi_half_times_three) or (start_angle > pi_half_times_three and start_angle + aperture > 7 * pi_half) then bottommost := bottom end if start_angle + aperture > pi_times_two then rightmost := right end create Result.make (leftmost, topmost, rightmost - leftmost, bottommost - topmost) end -- -- bounding_box: EV_RECTANGLE is -- -- Smallest orthogonal rectangular area `Current' fits in. -- local -- theta_start, theta_end: DOUBLE -- leftmost, rightmost, topmost, bottommost: INTEGER -- a, b, center_x, center_y: INTEGER -- top, left, bottom, right: INTEGER -- pax, pay, pbx, pby: INTEGER -- end_angle: DOUBLE -- cosine_theta_start, cosine_theta_end, sine_theta_start, sine_theta_end: DOUBLE -- do -- pax := point_a.x_abs -- pay := point_a.y_abs -- pbx := point_b.x_abs -- pby := point_b.y_abs -- -- end_angle := start_angle + aperture -- -- top := pay.min (pby) -- left := pax.min (pbx) -- bottom := pby.max (pay) -- right := pbx.max (pax) -- -- center_x := (left + right) // 2 -- center_y := (top + bottom) // 2 -- -- a := (right - left) // 2 -- b := (bottom - top) // 2 -- positive downwards -- -- -- The calculations for the bounding box start here -- -- Map the image angles to the parametric angles, correcting for the tangent -- theta_start := arc_tangent (a / b * tangent(start_angle)) -- if start_angle > pi_half and start_angle < pi_half_times_three then -- theta_start := theta_start + Pi -- end -- theta_end := arc_tangent(a / b * tangent(end_angle)) -- if modulo(end_angle, pi_times_two) > pi_half and modulo(end_angle, pi_times_two) < pi_half_times_three then -- theta_end := theta_end + Pi -- end -- -- -- Find the bounding box for the three points on the pie -- -- Watch the signs because conventions are not completely observed -- -- cosine_theta_start := cosine(theta_start) -- cosine_theta_end := cosine(theta_end) -- sine_theta_start := sine(theta_start) -- sine_theta_end := sine(theta_end) -- -- leftmost := (center_x + a * cosine_theta_start.min(cosine_theta_end)).floor -- rightmost := (center_x + a * cosine_theta_start.max(cosine_theta_end)).ceiling -- topmost := (center_y - b * sine_theta_start.max(sine_theta_end)).floor -- bottommost := (center_y - b * sine_theta_start.min(sine_theta_end)).ceiling -- -- -- Adjust for extreme excursions of the pie, when the box is tangent to the curve -- -- Assumes positve values for aperture and start_angle -- -- if -- (start_angle < pi_half and start_angle + aperture > pi_half) or -- (start_angle > pi_half and start_angle + aperture > 5 * pi_half) -- then -- topmost := top -- end -- -- if -- (start_angle < Pi and start_angle + aperture > Pi) or -- (start_angle > pi and start_angle + aperture > pi_times_three) -- then -- leftmost := left -- end -- -- if -- (start_angle < pi_times_three and start_angle + aperture > pi_times_three) or -- (start_angle > pi_times_three and start_angle + aperture > 7 * pi_half) -- then -- bottommost := bottom -- end -- -- if -- start_angle + aperture > pi_times_two -- then -- rightmost := right -- end -- -- create Result.make (leftmost, topmost, rightmost - leftmost, bottommost - topmost) -- end position_on_figure (x, y: INTEGER): BOOLEAN -- Is (`x', `y') on this figure? local ay, ax, bx, by, cx, cy, top, left: INTEGER angle, end_angle: DOUBLE angle_inside: BOOLEAN do if aperture /= 0.0 then ax := point_a.x_abs ay := point_a.y_abs bx := point_b.x_abs by := point_b.y_abs left := ax.min (bx) top := ay.min (by) cx := left + (bx - ax).abs // 2 cy := top + (by - ay).abs // 2 angle := line_angle (x, y, cx, cy) end_angle := modulo (start_angle + aperture, pi_times_two) if start_angle < end_angle then angle_inside := angle >= start_angle and angle <= end_angle else angle_inside := angle >= start_angle or angle <= end_angle end Result := point_on_ellipse_boundary (x, y, cx, cy, cx - left, cy - top, line_width ) and angle_inside else Result := False end end feature {EV_FIGURE_DRAWING_ROUTINES} -- Access metrics: TUPLE [INTEGER, INTEGER, INTEGER, INTEGER] -- [`top_left_x', `top_left_y', `width', `height'] local ay, ax, bx, by: INTEGER do ax := point_a.x_abs ay := point_a.y_abs bx := point_b.x_abs by := point_b.y_abs Result := [ax.min (bx), ay.min (by), (bx - ax).abs, (by - ay).abs] end invariant start_angle_within_bounds: start_angle >= 0 and then start_angle <= 2 * Pi aperture_within_bounds: aperture >= 0 and then aperture <= 2 * Pi note copyright: "Copyright (c) 1984-2006, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software 356 Storke Road, Goleta, CA 93117 USA Telephone 805-685-1006, Fax 805-685-6869 Website http://www.eiffel.com Customer support http://support.eiffel.com ]" end -- class EV_FIGURE_ARC