diff options
author | Tobias Christiansen <tobi@tobyase.de> | 2021-05-22 21:33:07 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-23 18:28:27 +0200 |
commit | bd2b17a70e3df4f44c302b320b36ee08a972b896 (patch) | |
tree | e4d1f33f7c8f8546ee9ce3433e4059443fc83af7 /Userland/Libraries/LibGfx | |
parent | 6a194869cf449bd62f661fb425ae3b7fc59df0de (diff) | |
download | serenity-bd2b17a70e3df4f44c302b320b36ee08a972b896.zip |
LibGfx: Replace ellipse drawing algorithm
The new one is way more naive and not as fancy as the old one, but it
doesn't crash when trying to draw circles.
This algorithm just sweeps the angles required by the call, makes sure
each segment is at most 1 (pixel) long and just uses the standard
parameterization to find the coordinates of each point on the ellipse.
Diffstat (limited to 'Userland/Libraries/LibGfx')
-rw-r--r-- | Userland/Libraries/LibGfx/Painter.cpp | 84 |
1 files changed, 37 insertions, 47 deletions
diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index 6fc5ca1c9c..685d94af07 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -1841,27 +1841,6 @@ void Painter::for_each_line_segment_on_bezier_curve(const FloatPoint& control_po for_each_line_segment_on_bezier_curve(control_point, p1, p2, callback); } -static bool can_approximate_elliptical_arc(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& center, const FloatPoint radii, float x_axis_rotation, float theta_1, float theta_delta) -{ - constexpr static float tolerance = 0.3f; - - auto half_theta_delta = theta_delta / 2.0f; - - auto xc = cosf(x_axis_rotation); - auto xs = sinf(x_axis_rotation); - auto tc = cosf(theta_1 + half_theta_delta); - auto ts = sinf(theta_1 + half_theta_delta); - - auto x2 = xc * radii.x() * tc - xs * radii.y() * ts + center.x(); - auto y2 = xs * radii.x() * tc + xc * radii.y() * ts + center.y(); - - auto ellipse_mid_point = FloatPoint { x2, y2 }; - auto line_mid_point = p1 + (p2 - p1) / 2.0f; - - auto v = ellipse_mid_point.distance_from(line_mid_point); - return v < tolerance; -} - void Painter::draw_quadratic_bezier_curve(const IntPoint& control_point, const IntPoint& p1, const IntPoint& p2, Color color, int thickness, LineStyle style) { VERIFY(scale() == 1); // FIXME: Add scaling support. @@ -1874,40 +1853,51 @@ void Painter::draw_quadratic_bezier_curve(const IntPoint& control_point, const I // static void Painter::for_each_line_segment_on_elliptical_arc(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& center, const FloatPoint radii, float x_axis_rotation, float theta_1, float theta_delta, Function<void(const FloatPoint&, const FloatPoint&)>& callback) { - struct SegmentDescriptor { - FloatPoint p1; - FloatPoint p2; - float theta; - float theta_delta; - }; + if (radii.x() <= 0 || radii.y() <= 0) + return; - static constexpr auto split_elliptical_arc = [](const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& center, const FloatPoint radii, float x_axis_rotation, float theta_1, float theta_delta, auto& segments) { - auto half_theta_delta = theta_delta / 2; - auto theta_mid = theta_1 + half_theta_delta; + auto start = p1; + auto end = p2; - auto xc = cosf(x_axis_rotation); - auto xs = sinf(x_axis_rotation); - auto tc = cosf(theta_1 + half_theta_delta); - auto ts = sinf(theta_1 + half_theta_delta); + if (theta_delta < 0) { + swap(start, end); + theta_1 = theta_1 + theta_delta; + theta_delta = fabs(theta_delta); + } + + auto relative_start = start - center; + + auto a = radii.x(); + auto b = radii.y(); - auto x2 = xc * radii.x() * tc - xs * radii.y() * ts + center.x(); - auto y2 = xs * radii.x() * tc + xc * radii.y() * ts + center.y(); + // The segments are at most 1 long + auto largest_radius = max(a, b); + double theta_step = atan(1 / (double)largest_radius); - FloatPoint mid_point = { x2, y2 }; + FloatPoint current_point = relative_start; + FloatPoint next_point = { 0, 0 }; - segments.enqueue({ p1, mid_point, theta_1, half_theta_delta }); - segments.enqueue({ mid_point, p2, theta_mid, half_theta_delta }); + auto sin_x_axis = sinf(x_axis_rotation); + auto cos_x_axis = cosf(x_axis_rotation); + auto rotate_point = [sin_x_axis, cos_x_axis](FloatPoint& p) { + auto original_x = p.x(); + auto original_y = p.y(); + + p.set_x(original_x * cos_x_axis - original_y * sin_x_axis); + p.set_y(original_x * sin_x_axis + original_y * cos_x_axis); }; - Queue<SegmentDescriptor> segments; - segments.enqueue({ p1, p2, theta_1, theta_delta }); - while (!segments.is_empty()) { - auto segment = segments.dequeue(); - if (can_approximate_elliptical_arc(segment.p1, segment.p2, center, radii, x_axis_rotation, segment.theta, segment.theta_delta)) - callback(segment.p1, segment.p2); - else - split_elliptical_arc(segment.p1, segment.p2, center, radii, x_axis_rotation, segment.theta, segment.theta_delta, segments); + for (double theta = theta_1; theta <= ((double)theta_1 + (double)theta_delta); theta += theta_step) { + next_point.set_x(a * cosf(theta)); + next_point.set_y(b * sinf(theta)); + rotate_point(next_point); + + callback(current_point + center, next_point + center); + + current_point = next_point; } + + callback(current_point + center, end); } // static |