[gtk/path-work-rebased: 62/149] path: Implement SVG arcs




commit adfc4b07a269e6d2940530b22ff8ba1ebc665a45
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 bba6dff6d0..40b9fc3c88 100644
--- a/gsk/gskpathbuilder.c
+++ b/gsk/gskpathbuilder.c
@@ -568,3 +568,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]