summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibGfx/Painter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibGfx/Painter.cpp')
-rw-r--r--Userland/Libraries/LibGfx/Painter.cpp84
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