[gtk/wip/otte/lottie: 11/11] WIP: path: Add conic curves




commit c82080ccea0e8c843209fb78c8b1673d4bc73ea9
Author: Benjamin Otte <otte redhat com>
Date:   Sat Nov 28 07:24:05 2020 +0100

    WIP: path: Add conic curves
    
    So far this just adds the API, if you use it, you'll get lots of
    g_warnings().

 demos/gtk-demo/path_text.c           |  10 ++++
 docs/reference/gsk/gsk4-sections.txt |   1 +
 gsk/gskenums.h                       |   4 ++
 gsk/gskpath.c                        | 110 +++++++++++++++++++++++++++++++----
 gsk/gskpath.h                        |   2 +
 gsk/gskpathbuilder.c                 |  35 +++++++++++
 gsk/gskpathbuilder.h                 |   7 +++
 7 files changed, 158 insertions(+), 11 deletions(-)
---
diff --git a/demos/gtk-demo/path_text.c b/demos/gtk-demo/path_text.c
index 22e57f3eed..144e13a58c 100644
--- a/demos/gtk-demo/path_text.c
+++ b/demos/gtk-demo/path_text.c
@@ -103,6 +103,7 @@ static gboolean
 gtk_path_transform_op (GskPathOperation        op,
                        const graphene_point_t *pts,
                        gsize                   n_pts,
+                       float                   weight,
                        gpointer                data)
 {
   GtkPathTransform *transform = data;
@@ -135,6 +136,15 @@ gtk_path_transform_op (GskPathOperation        op,
       }
       break;
 
+    case GSK_PATH_CONIC:
+      {
+        graphene_point_t res[2];
+        gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
+        gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
+        gsk_path_builder_conic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, weight);
+      }
+      break;
+
     case GSK_PATH_CLOSE:
       gsk_path_builder_close (transform->builder);
       break;
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index 5ad68326d4..5d34f26e6e 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -296,6 +296,7 @@ gsk_path_builder_add_rect
 gsk_path_builder_move_to
 gsk_path_builder_line_to
 gsk_path_builder_curve_to
+gsk_path_builder_conic_to
 gsk_path_builder_close
 <SUBSECTION Private>
 GSK_TYPE_PATH_BUILDER
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 8ca577563d..cb4fa5fe13 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -244,6 +244,9 @@ typedef enum {
  * @GSK_PATH_CURVE: A curve-to operation describing a cubic bezier curve
  *     with 4 points describing the start point, the two control points
  *     and the end point of the curve.
+ * @GSK_PATH_CONIC: A weighted quadratic bezier curve with 3 points
+ *     describing the start point, control point and end point of the
+ *     curve. A weight for the curve will be passed, too.
  *
  * Path operations can be used to approximate a #GskPath.
  **/
@@ -252,6 +255,7 @@ typedef enum {
   GSK_PATH_CLOSE,
   GSK_PATH_LINE,
   GSK_PATH_CURVE,
+  GSK_PATH_CONIC,
 } GskPathOperation;
 
 /**
diff --git a/gsk/gskpath.c b/gsk/gskpath.c
index 0f6fbc8ef1..c15d457ab4 100644
--- a/gsk/gskpath.c
+++ b/gsk/gskpath.c
@@ -246,11 +246,11 @@ gsk_rect_contour_foreach (const GskContour   *contour,
     GRAPHENE_POINT_INIT (self->x,               self->y)
   };
 
-  return func (GSK_PATH_MOVE, &pts[0], 1, user_data)
-      && func (GSK_PATH_LINE, &pts[0], 2, user_data)
-      && func (GSK_PATH_LINE, &pts[1], 2, user_data)
-      && func (GSK_PATH_LINE, &pts[2], 2, user_data)
-      && func (GSK_PATH_CLOSE, &pts[3], 2, user_data);
+  return func (GSK_PATH_MOVE, &pts[0], 1, 0, user_data)
+      && func (GSK_PATH_LINE, &pts[0], 2, 0, user_data)
+      && func (GSK_PATH_LINE, &pts[1], 2, 0, user_data)
+      && func (GSK_PATH_LINE, &pts[2], 2, 0, user_data)
+      && func (GSK_PATH_CLOSE, &pts[3], 2, 0, user_data);
 }
 
 static gpointer
@@ -605,7 +605,7 @@ gsk_circle_contour_curve (const graphene_point_t curve[4],
 {
   ForeachWrapper *wrapper = data;
 
-  return wrapper->func (GSK_PATH_CURVE, curve, 4, wrapper->user_data);
+  return wrapper->func (GSK_PATH_CURVE, curve, 4, 0, wrapper->user_data);
 }
 
 static gboolean
@@ -617,7 +617,7 @@ gsk_circle_contour_foreach (const GskContour   *contour,
   const GskCircleContour *self = (const GskCircleContour *) contour;
   graphene_point_t start = GSK_CIRCLE_POINT_INIT (self, self->start_angle);
 
-  if (!func (GSK_PATH_MOVE, &start, 1, user_data))
+  if (!func (GSK_PATH_MOVE, &start, 1, 0, user_data))
     return FALSE;
 
   if (!gsk_spline_decompose_arc (&self->center,
@@ -631,7 +631,7 @@ gsk_circle_contour_foreach (const GskContour   *contour,
 
   if (fabs (self->start_angle - self->end_angle) >= 360)
     {
-      if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, user_data))
+      if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, 0, user_data))
         return FALSE;
     }
 
@@ -862,13 +862,25 @@ gsk_standard_contour_foreach (const GskContour   *contour,
     [GSK_PATH_MOVE] = 1,
     [GSK_PATH_CLOSE] = 2,
     [GSK_PATH_LINE] = 2,
-    [GSK_PATH_CURVE] = 4
+    [GSK_PATH_CURVE] = 4,
   };
 
   for (i = 0; i < self->n_ops; i ++)
     {
-      if (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], user_data))
-        return FALSE;
+      if (self->ops[i].op == GSK_PATH_CONIC)
+        {
+          graphene_point_t pts[2] = { self->points[self->ops[i].point],
+                                      self->points[self->ops[i].point + 2] };
+          float weight = self->points[self->ops[i].point + 1].x;
+
+          if (!func (GSK_PATH_CONIC, pts, 2, weight, user_data))
+            return FALSE;
+        }
+      else
+        {
+          if (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], 0, 
user_data))
+            return FALSE;
+        }
     }
 
   return TRUE;
@@ -918,6 +930,16 @@ gsk_standard_contour_print (const GskContour *contour,
           _g_string_append_point (string, &pt[3]);
           break;
 
+        case GSK_PATH_CONIC:
+          /* This is not valid SVG */
+          g_string_append (string, " O ");
+          _g_string_append_point (string, &pt[1]);
+          g_string_append (string, ", ");
+          _g_string_append_double (string, pt[2].x);
+          g_string_append (string, ", ");
+          _g_string_append_point (string, &pt[3]);
+          break;
+
         default:
           g_assert_not_reached();
           return;
@@ -1070,6 +1092,23 @@ gsk_standard_contour_init_measure (const GskContour *contour,
           }
           break;
 
+        case GSK_PATH_CONIC:
+          g_warning ("FIXME: Stop treating conics as lines");
+          seg_length = graphene_point_distance (&pt[0], &pt[3], NULL, NULL);
+          if (seg_length > 0)
+            {
+              g_array_append_vals (array,
+                                   &(GskStandardContourMeasure) {
+                                     length, 
+                                     length + seg_length,
+                                     0, 1,
+                                     pt[1],
+                                     i,
+                                   }, 1);
+              length += seg_length;
+            }
+          break;
+
         default:
           g_assert_not_reached();
           return NULL;
@@ -1130,6 +1169,16 @@ gsk_standard_contour_measure_get_point (GskStandardContour        *self,
         gsk_spline_get_point_cubic (pts, progress, pos, tangent);
         break;
 
+      case GSK_PATH_CONIC:
+        g_warning ("FIXME: Stop treating conics as lines");
+        if (pos)
+          graphene_point_interpolate (&pts[0], &pts[3], progress, pos);
+        if (tangent)
+          {
+            graphene_vec2_init (tangent, pts[3].x - pts[0].x, pts[3].y - pts[0].y);
+            graphene_vec2_normalize (tangent, tangent);
+          }
+        break;
       case GSK_PATH_MOVE:
       default:
         g_assert_not_reached ();
@@ -1363,6 +1412,24 @@ gsk_standard_contour_add_segment (const GskContour *contour,
           }
           break;
 
+        case GSK_PATH_CONIC:
+          g_warning ("FIXME: Stop treating conics as lines");
+          {
+            graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
+            graphene_point_t point;
+
+            graphene_point_interpolate (&pts[0], &pts[3], start_progress, &point);
+            gsk_path_builder_move_to (builder, point.x, point.y);
+            if (end_measure && end_measure->op == start_measure->op)
+              {
+                graphene_point_interpolate (&pts[0], &pts[3], end_progress, &point);
+                gsk_path_builder_line_to (builder, point.x, point.y);
+                return;
+              }
+            gsk_path_builder_line_to (builder, pts[3].x, pts[3].y);
+          }
+          break;
+
         case GSK_PATH_MOVE:
         default:
           g_assert_not_reached();
@@ -1392,6 +1459,10 @@ gsk_standard_contour_add_segment (const GskContour *contour,
           gsk_path_builder_curve_to (builder, pt[1].x, pt[1].y, pt[2].x, pt[2].y, pt[3].x, pt[3].y);
           break;
 
+        case GSK_PATH_CONIC:
+          gsk_path_builder_conic_to (builder, pt[1].x, pt[1].y, pt[3].x, pt[3].y, pt[2].x);
+          break;
+
         default:
           g_assert_not_reached();
           return;
@@ -1424,6 +1495,17 @@ gsk_standard_contour_add_segment (const GskContour *contour,
           }
           break;
 
+        case GSK_PATH_CONIC:
+          g_warning ("FIXME: Stop treating conics as lines");
+          {
+            graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
+            graphene_point_t point;
+
+            graphene_point_interpolate (&pts[0], &pts[3], end_progress, &point);
+            gsk_path_builder_line_to (builder, point.x, point.y);
+          }
+          break;
+
         case GSK_PATH_MOVE:
         default:
           g_assert_not_reached();
@@ -1804,6 +1886,7 @@ static gboolean
 gsk_path_to_cairo_add_op (GskPathOperation        op,
                           const graphene_point_t *pts,
                           gsize                   n_pts,
+                          float                   weight,
                           gpointer                cr)
 {
   switch (op)
@@ -1824,6 +1907,11 @@ gsk_path_to_cairo_add_op (GskPathOperation        op,
       cairo_curve_to (cr, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
       break;
 
+    case GSK_PATH_CONIC:
+      g_warning ("FIXME: Stop treating conics as lines");
+      cairo_line_to (cr, pts[3].x, pts[3].y);
+      break;
+
     default:
       g_assert_not_reached ();
       return FALSE;
diff --git a/gsk/gskpath.h b/gsk/gskpath.h
index 57bdc5bba2..d0116e7ba4 100644
--- a/gsk/gskpath.h
+++ b/gsk/gskpath.h
@@ -34,6 +34,7 @@ G_BEGIN_DECLS
  * @op: The operation to perform
  * @pts: The points of the operation
  * @n_pts: The number of points
+ * @weight: The weight for conic curves, or unused if not a conic curve.
  * @user_data: The user data provided with the function
  *
  * Prototype of the callback to iterate throught the operations of
@@ -45,6 +46,7 @@ G_BEGIN_DECLS
 typedef gboolean (* GskPathForeachFunc) (GskPathOperation        op,
                                          const graphene_point_t *pts,
                                          gsize                   n_pts,
+                                         float                   weight,
                                          gpointer                user_data);
 
 #define GSK_TYPE_PATH (gsk_path_get_type ())
diff --git a/gsk/gskpathbuilder.c b/gsk/gskpathbuilder.c
index 16242a306d..161046157c 100644
--- a/gsk/gskpathbuilder.c
+++ b/gsk/gskpathbuilder.c
@@ -446,6 +446,41 @@ gsk_path_builder_curve_to (GskPathBuilder *builder,
                                    });
 }
 
+/**
+ * gsk_path_builder_conic_to:
+ * @builder: a #GskPathBuilder
+ * @x1: x coordinate of control point
+ * @y1: y coordinate of control point
+ * @x2: x coordinate of the end of the curve
+ * @y2: y coordinate of the end of the curve
+ * @weight: weight of the curve
+ *
+ * Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
+ * from the current point to @x2, @y2 with the given
+ * @weight and @x1, @y1 as the single control point.
+ *
+ * Conic curves can be used to draw ellipses and circles.
+ **/
+void
+gsk_path_builder_conic_to (GskPathBuilder *builder,
+                           float           x1,
+                           float           y1,
+                           float           x2,
+                           float           y2,
+                           float           weight)
+{
+  g_return_if_fail (builder != NULL);
+
+  builder->flags ^= ~GSK_PATH_FLAT;
+  gsk_path_builder_append_current (builder,
+                                   GSK_PATH_CONIC,
+                                   3, (graphene_point_t[3]) {
+                                     GRAPHENE_POINT_INIT (x1, y1),
+                                     GRAPHENE_POINT_INIT (weight, 0),
+                                     GRAPHENE_POINT_INIT (x2, y2)
+                                   });
+}
+
 /**
  * gsk_path_builder_close:
  * @builder: a #GskPathBuilder
diff --git a/gsk/gskpathbuilder.h b/gsk/gskpathbuilder.h
index 2bc7a3933d..cca8366430 100644
--- a/gsk/gskpathbuilder.h
+++ b/gsk/gskpathbuilder.h
@@ -73,6 +73,13 @@ void                    gsk_path_builder_curve_to               (GskPathBuilder
                                                                  float                   x3,
                                                                  float                   y3);
 GDK_AVAILABLE_IN_ALL
+void                    gsk_path_builder_conic_to               (GskPathBuilder         *builder,
+                                                                 float                   x1,
+                                                                 float                   y1,
+                                                                 float                   x2,
+                                                                 float                   y2,
+                                                                 float                   weight);
+GDK_AVAILABLE_IN_ALL
 void                    gsk_path_builder_close                  (GskPathBuilder         *builder);
 
 G_END_DECLS


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]