summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2021-04-15 03:56:04 +0430
committerAndreas Kling <kling@serenityos.org>2021-04-15 17:50:16 +0200
commit1ea466661f83f8866dfbe60307d015aaafddfb8f (patch)
treefc123c497bf4049626dcb9fca3702b15aa467cd2 /Userland/Libraries
parentcb04a441cf1d581585aecc05bac22e42b254a252 (diff)
downloadserenity-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.cpp2
-rw-r--r--Userland/Libraries/LibGfx/Path.cpp87
-rw-r--r--Userland/Libraries/LibGfx/Path.h6
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp69
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;
}