[gtk/wip/otte/lottie: 25/43] path: Implement SVG arcs
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/lottie: 25/43] path: Implement SVG arcs
- Date: Sun, 6 Dec 2020 17:57:15 +0000 (UTC)
commit 13153e4b3081a1f53ba7e5bda20cee10ee55d5a4
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Nov 28 12:07:12 2020 -0500
path: Implement SVG arcs
This is elliptical arc implementation according to the SVG spec.
The code is mostly taken from librsvg, but pretty directly
follows the SVG spec implementation notes.
We don't export this, since the parametrization is inconvenient.
We do want an arc_to API, but these are not the arcs we are
looking for.
It will be used in parsing SVG path syntax.
gsk/gskpathbuilder.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++
gsk/gskpathprivate.h | 9 +++
2 files changed, 172 insertions(+)
---
diff --git a/gsk/gskpathbuilder.c b/gsk/gskpathbuilder.c
index f49a27c8a5..18d62ff73a 100644
--- a/gsk/gskpathbuilder.c
+++ b/gsk/gskpathbuilder.c
@@ -579,3 +579,166 @@ gsk_path_builder_close (GskPathBuilder *builder)
gsk_path_builder_end_current (builder);
}
+
+static void
+arc_segment (GskPathBuilder *builder,
+ double cx,
+ double cy,
+ double rx,
+ double ry,
+ double sin_phi,
+ double cos_phi,
+ double sin_th0,
+ double cos_th0,
+ double sin_th1,
+ double cos_th1,
+ double t)
+{
+ double x1, y1, x2, y2, x3, y3;
+
+ x1 = rx * (cos_th0 - t * sin_th0);
+ y1 = ry * (sin_th0 + t * cos_th0);
+ x3 = rx * cos_th1;
+ y3 = ry * sin_th1;
+ x2 = x3 + rx * (t * sin_th1);
+ y2 = y3 + ry * (-t * cos_th1);
+
+ gsk_path_builder_curve_to (builder,
+ cx + cos_phi * x1 - sin_phi * y1,
+ cy + sin_phi * x1 + cos_phi * y1,
+ cx + cos_phi * x2 - sin_phi * y2,
+ cy + sin_phi * x2 + cos_phi * y2,
+ cx + cos_phi * x3 - sin_phi * y3,
+ cy + sin_phi * x3 + cos_phi * y3);
+}
+
+void
+gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
+ float rx,
+ float ry,
+ float x_axis_rotation,
+ gboolean large_arc,
+ gboolean positive_sweep,
+ float x,
+ float y)
+{
+ graphene_point_t *current;
+ double x1, y1, x2, y2;
+ double phi, sin_phi, cos_phi;
+ double mid_x, mid_y;
+ double lambda;
+ double d;
+ double k;
+ double x1_, y1_;
+ double cx_, cy_;
+ double cx, cy;
+ double ux, uy, u_len;
+ double cos_theta1, theta1;
+ double vx, vy, v_len;
+ double dp_uv;
+ double cos_delta_theta, delta_theta;
+ int i, n_segs;
+ double d_theta, theta;
+ double sin_th0, cos_th0;
+ double sin_th1, cos_th1;
+ double th_half;
+ double t;
+
+ if (builder->points->len > 0)
+ {
+ current = &g_array_index (builder->points, graphene_point_t, builder->points->len - 1);
+ x1 = current->x;
+ y1 = current->y;
+ }
+ else
+ {
+ x1 = 0;
+ y1 = 0;
+ }
+ x2 = x;
+ y2 = y;
+
+ phi = x_axis_rotation * M_PI / 180.0;
+ sincos (phi, &sin_phi, &cos_phi);
+
+ rx = fabs (rx);
+ ry = fabs (ry);
+
+ mid_x = (x1 - x2) / 2;
+ mid_y = (y1 - y2) / 2;
+
+ x1_ = cos_phi * mid_x + sin_phi * mid_y;
+ y1_ = - sin_phi * mid_x + cos_phi * mid_y;
+
+ lambda = (x1_ / rx) * (x1_ / rx) + (y1_ / ry) * (y1_ / ry);
+ if (lambda > 1)
+ {
+ lambda = sqrt (lambda);
+ rx *= lambda;
+ ry *= lambda;
+ }
+
+ d = (rx * y1_) * (rx * y1_) + (ry * x1_) * (ry * x1_);
+ if (d == 0)
+ return;
+
+ k = sqrt (fabs ((rx * ry) * (rx * ry) / d - 1.0));
+ if (positive_sweep == large_arc)
+ k = -k;
+
+ cx_ = k * rx * y1_ / ry;
+ cy_ = -k * ry * x1_ / rx;
+
+ cx = cos_phi * cx_ - sin_phi * cy_ + (x1 + x2) / 2;
+ cy = sin_phi * cx_ + cos_phi * cy_ + (y1 + y2) / 2;
+
+ ux = (x1_ - cx_) / rx;
+ uy = (y1_ - cy_) / ry;
+ u_len = sqrt (ux * ux + uy * uy);
+ if (u_len == 0)
+ return;
+
+ cos_theta1 = CLAMP (ux / u_len, -1, 1);
+ theta1 = acos (cos_theta1);
+ if (uy < 0)
+ theta1 = - theta1;
+
+ vx = (- x1_ - cx_) / rx;
+ vy = (- y1_ - cy_) / ry;
+ v_len = sqrt (vx * vx + vy * vy);
+ if (v_len == 0)
+ return;
+
+ dp_uv = ux * vx + uy * vy;
+ cos_delta_theta = CLAMP (dp_uv / (u_len * v_len), -1, 1);
+ delta_theta = acos (cos_delta_theta);
+ if (ux * vy - uy * vx < 0)
+ delta_theta = - delta_theta;
+ if (positive_sweep && delta_theta < 0)
+ delta_theta += 2 * M_PI;
+ else if (!positive_sweep && delta_theta > 0)
+ delta_theta -= 2 * M_PI;
+
+ n_segs = ceil (fabs (delta_theta / (M_PI_2 + 0.001)));
+ d_theta = delta_theta / n_segs;
+ theta = theta1;
+ sincos (theta1, &sin_th1, &cos_th1);
+
+ th_half = d_theta / 2;
+ t = (8.0 / 3.0) * sin (th_half / 2) * sin (th_half / 2) / sin (th_half);
+
+ for (i = 0; i < n_segs; i++)
+ {
+ theta = theta1;
+ theta1 = theta + d_theta;
+ sin_th0 = sin_th1;
+ cos_th0 = cos_th1;
+ sincos (theta1, &sin_th1, &cos_th1);
+ arc_segment (builder,
+ cx, cy, rx, ry,
+ sin_phi, cos_phi,
+ sin_th0, cos_th0,
+ sin_th1, cos_th1,
+ t);
+ }
+}
diff --git a/gsk/gskpathprivate.h b/gsk/gskpathprivate.h
index cdb4a76243..913c9c759e 100644
--- a/gsk/gskpathprivate.h
+++ b/gsk/gskpathprivate.h
@@ -101,6 +101,15 @@ void gsk_contour_add_segment (const GskContou
void gsk_path_builder_add_contour (GskPathBuilder *builder,
GskContour *contour);
+void gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
+ float rx,
+ float ry,
+ float x_axis_rotation,
+ gboolean large_arc,
+ gboolean positive_sweep,
+ float x,
+ float y);
+
G_END_DECLS
#endif /* __GSK_PATH_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]