[gtk/matthiasc/lottie2: 3/8] path: Implement gsk_path_builder_arc_to
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/matthiasc/lottie2: 3/8] path: Implement gsk_path_builder_arc_to
- Date: Wed, 25 Nov 2020 15:17:26 +0000 (UTC)
commit 391b7b016f371f98617e5f1b3493b6ce7b04772b
Author: Matthias Clasen <mclasen redhat com>
Date: Tue Nov 24 19:59:46 2020 -0500
path: Implement gsk_path_builder_arc_to
This is an elliptical arc implementation according
to the SVG spec. The code is mostly taken from
librsvg, but pretty directly follows the SVG spec
implementation notes.
gsk/gskpath.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gsk/gskpath.h | 11 ++++
2 files changed, 173 insertions(+)
---
diff --git a/gsk/gskpath.c b/gsk/gskpath.c
index 6f3a988c62..1082813ddd 100644
--- a/gsk/gskpath.c
+++ b/gsk/gskpath.c
@@ -2168,3 +2168,165 @@ 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_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/gskpath.h b/gsk/gskpath.h
index f2010d5397..d2dd03c927 100644
--- a/gsk/gskpath.h
+++ b/gsk/gskpath.h
@@ -131,6 +131,17 @@ void gsk_path_builder_curve_to (GskPathBuilder
float y2,
float x3,
float y3);
+
+GDK_AVAILABLE_IN_ALL
+void gsk_path_builder_arc_to (GskPathBuilder *builder,
+ float rx,
+ float ry,
+ float x_axis_rotation,
+ gboolean large_arc,
+ gboolean positive_sweep,
+ float x,
+ float y);
+
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_close (GskPathBuilder *builder);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]