1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 13:18:13 +00:00

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 :^)
This commit is contained in:
AnotherTest 2021-04-15 03:56:04 +04:30 committed by Andreas Kling
parent cb04a441cf
commit 1ea466661f
4 changed files with 90 additions and 74 deletions

View file

@ -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;
}