diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2021-04-15 03:56:04 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-04-15 17:50:16 +0200 |
commit | 1ea466661f83f8866dfbe60307d015aaafddfb8f (patch) | |
tree | fc123c497bf4049626dcb9fca3702b15aa467cd2 /Userland/Libraries | |
parent | cb04a441cf1d581585aecc05bac22e42b254a252 (diff) | |
download | serenity-1ea466661f83f8866dfbe60307d015aaafddfb8f.zip |
LibGfx+LibWeb: Move out the EllipticArcTo() logic into Path
At its previous state, the interface allowed invalid "ellipses" to be
specified, instead of doing that, simply use the parameters that SVG
uses :^)
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibGfx/Painter.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/Path.cpp | 87 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/Path.h | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp | 69 |
4 files changed, 90 insertions, 74 deletions
diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index f3fb042a32..b3a383dc3d 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -1492,7 +1492,7 @@ void Painter::for_each_line_segment_on_bezier_curve(const FloatPoint& control_po 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 = 1; + constexpr static float tolerance = 0.5f; auto half_theta_delta = theta_delta / 2.0f; diff --git a/Userland/Libraries/LibGfx/Path.cpp b/Userland/Libraries/LibGfx/Path.cpp index 95812cf532..f7d758a794 100644 --- a/Userland/Libraries/LibGfx/Path.cpp +++ b/Userland/Libraries/LibGfx/Path.cpp @@ -33,6 +33,93 @@ namespace Gfx { +void Path::elliptical_arc_to(const FloatPoint& next_point, const FloatPoint& radii, double x_axis_rotation, bool large_arc, bool sweep) +{ + double rx = radii.x(); + double ry = radii.y(); + + double x_axis_rotation_c = cos(x_axis_rotation); + double x_axis_rotation_s = sin(x_axis_rotation); + + // Find the last point + FloatPoint last_point { 0, 0 }; + if (!m_segments.is_empty()) + last_point = m_segments.last().point(); + + // Step 1 of out-of-range radii correction + if (rx == 0.0 || ry == 0.0) { + append_segment<LineSegment>(next_point); + return; + } + + // Step 2 of out-of-range radii correction + if (rx < 0) + rx *= -1.0; + if (ry < 0) + ry *= -1.0; + + // Find (cx, cy), theta_1, theta_delta + // Step 1: Compute (x1', y1') + auto x_avg = (last_point.x() - next_point.x()) / 2.0f; + auto y_avg = (last_point.y() - next_point.y()) / 2.0f; + auto x1p = x_axis_rotation_c * x_avg + x_axis_rotation_s * y_avg; + auto y1p = -x_axis_rotation_s * x_avg + x_axis_rotation_c * y_avg; + + // Step 2: Compute (cx', cy') + double x1p_sq = pow(x1p, 2.0); + double y1p_sq = pow(y1p, 2.0); + double rx_sq = pow(rx, 2.0); + double ry_sq = pow(ry, 2.0); + + // Step 3 of out-of-range radii correction + double lambda = x1p_sq / rx_sq + y1p_sq / ry_sq; + double multiplier; + + if (lambda > 1.0) { + auto lambda_sqrt = sqrt(lambda); + rx *= lambda_sqrt; + ry *= lambda_sqrt; + multiplier = 0.0; + } else { + double numerator = rx_sq * ry_sq - rx_sq * y1p_sq - ry_sq * x1p_sq; + double denominator = rx_sq * y1p_sq + ry_sq * x1p_sq; + multiplier = sqrt(numerator / denominator); + } + + if (large_arc == sweep) + multiplier *= -1.0; + + double cxp = multiplier * rx * y1p / ry; + double cyp = multiplier * -ry * x1p / rx; + + // Step 3: Compute (cx, cy) from (cx', cy') + x_avg = (last_point.x() + next_point.x()) / 2.0f; + y_avg = (last_point.y() + next_point.y()) / 2.0f; + double cx = x_axis_rotation_c * cxp - x_axis_rotation_s * cyp + x_avg; + double cy = x_axis_rotation_s * cxp + x_axis_rotation_c * cyp + y_avg; + + double theta_1 = atan2((y1p - cyp) / ry, (x1p - cxp) / rx); + double theta_2 = atan2((-y1p - cyp) / ry, (-x1p - cxp) / rx); + + auto theta_delta = theta_2 - theta_1; + + if (!sweep && theta_delta > 0.0f) { + theta_delta -= M_TAU; + } else if (sweep && theta_delta < 0) { + theta_delta += M_TAU; + } + + append_segment<EllipticalArcSegment>( + next_point, + FloatPoint(cx, cy), + FloatPoint(rx, ry), + static_cast<float>(x_axis_rotation), + static_cast<float>(theta_1), + static_cast<float>(theta_delta)); + + invalidate_split_lines(); +} + void Path::close() { if (m_segments.size() <= 1) diff --git a/Userland/Libraries/LibGfx/Path.h b/Userland/Libraries/LibGfx/Path.h index 0baf79e308..b9dd0b641d 100644 --- a/Userland/Libraries/LibGfx/Path.h +++ b/Userland/Libraries/LibGfx/Path.h @@ -154,11 +154,7 @@ public: invalidate_split_lines(); } - void elliptical_arc_to(const FloatPoint& point, const FloatPoint& center, const FloatPoint& radii, float x_axis_rotation, float theta_1, float theta_delta) - { - append_segment<EllipticalArcSegment>(point, center, radii, x_axis_rotation, theta_1, theta_delta); - invalidate_split_lines(); - } + void elliptical_arc_to(const FloatPoint& point, const FloatPoint& radii, double x_axis_rotation, bool large_arc, bool sweep); void close(); void close_all_subpaths(); diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp index 07773c0f72..a6a718aa8d 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp @@ -519,10 +519,6 @@ Gfx::Path& SVGPathElement::get_path() double x_axis_rotation = data[2] * M_DEG2RAD; double large_arc_flag = data[3]; double sweep_flag = data[4]; - - double x_axis_rotation_c = cos(x_axis_rotation); - double x_axis_rotation_s = sin(x_axis_rotation); - auto& last_point = path.segments().last().point(); Gfx::FloatPoint next_point; @@ -533,70 +529,7 @@ Gfx::Path& SVGPathElement::get_path() next_point = { data[5] + last_point.x(), data[6] + last_point.y() }; } - // Step 1 of out-of-range radii correction - if (rx == 0.0 || ry == 0.0) { - path.line_to(next_point); - break; - } - - // Step 2 of out-of-range radii correction - if (rx < 0) - rx *= -1.0; - if (ry < 0) - ry *= -1.0; - - // Find (cx, cy), theta_1, theta_delta - // Step 1: Compute (x1', y1') - auto x_avg = (last_point.x() - next_point.x()) / 2.0f; - auto y_avg = (last_point.y() - next_point.y()) / 2.0f; - auto x1p = x_axis_rotation_c * x_avg + x_axis_rotation_s * y_avg; - auto y1p = -x_axis_rotation_s * x_avg + x_axis_rotation_c * y_avg; - - // Step 2: Compute (cx', cy') - double x1p_sq = pow(x1p, 2.0); - double y1p_sq = pow(y1p, 2.0); - double rx_sq = pow(rx, 2.0); - double ry_sq = pow(ry, 2.0); - - // Step 3 of out-of-range radii correction - double lambda = x1p_sq / rx_sq + y1p_sq / ry_sq; - double multiplier; - - if (lambda > 1.0) { - auto lambda_sqrt = sqrt(lambda); - rx *= lambda_sqrt; - ry *= lambda_sqrt; - multiplier = 0.0; - } else { - double numerator = rx_sq * ry_sq - rx_sq * y1p_sq - ry_sq * x1p_sq; - double denominator = rx_sq * y1p_sq + ry_sq * x1p_sq; - multiplier = sqrt(numerator / denominator); - } - - if (large_arc_flag == sweep_flag) - multiplier *= -1.0; - - double cxp = multiplier * rx * y1p / ry; - double cyp = multiplier * -ry * x1p / rx; - - // Step 3: Compute (cx, cy) from (cx', cy') - x_avg = (last_point.x() + next_point.x()) / 2.0f; - y_avg = (last_point.y() + next_point.y()) / 2.0f; - double cx = x_axis_rotation_c * cxp - x_axis_rotation_s * cyp + x_avg; - double cy = x_axis_rotation_s * cxp + x_axis_rotation_c * cyp + y_avg; - - double theta_1 = atan2((y1p - cyp) / ry, (x1p - cxp) / rx); - double theta_2 = atan2((-y1p - cyp) / ry, (-x1p - cxp) / rx); - - auto theta_delta = theta_2 - theta_1; - - if (sweep_flag == 0 && theta_delta > 0.0f) { - theta_delta -= M_TAU; - } else if (sweep_flag != 0 && theta_delta < 0) { - theta_delta += M_TAU; - } - - path.elliptical_arc_to(next_point, { cx, cy }, { rx, ry }, x_axis_rotation, theta_1, theta_delta); + path.elliptical_arc_to(next_point, { rx, ry }, x_axis_rotation, large_arc_flag != 0, sweep_flag != 0); break; } |