[gtk/wip/matthiasc/lottie-stroke] wip: conic math debug



commit ca7014a20524a7e77dbfa5ff41bcde86a4036b81
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Dec 2 22:43:45 2020 -0500

    wip: conic math debug

 tests/curve2.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 367 insertions(+)
---
diff --git a/tests/curve2.c b/tests/curve2.c
index a9706b0ab3..f137f11c87 100644
--- a/tests/curve2.c
+++ b/tests/curve2.c
@@ -99,6 +99,322 @@ demo_widget_init (DemoWidget *self)
   gtk_widget_add_controller (GTK_WIDGET (self), controller);
 }
 
+typedef struct {
+  graphene_point_t p[3];
+  float w;
+} Conic;
+
+static gboolean
+get_conic_segment (GskPathOperation        op,
+                   const graphene_point_t *points,
+                   gsize                   length,
+                   float                   weight,
+                   gpointer                user_data)
+{
+  Conic *data = user_data;
+
+  if (op == GSK_PATH_CONIC)
+    {
+      if (data->w != 0)
+        {
+          data->w = 0;
+          return TRUE;
+        }
+
+      data->p[0] = points[0];
+      data->p[1] = points[1];
+      data->p[2] = points[2];
+      data->w = weight;
+
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+get_conic_shoulder_point (const graphene_point_t  p[3],
+                          float                   w,
+                          graphene_point_t       *q)
+{
+  graphene_point_t m;
+
+  graphene_point_interpolate (&p[0], &p[2], 0.5, &m);
+  graphene_point_interpolate (&m, &p[1], w / (1 + w), q);
+}
+
+static void
+get_rational_bezier (const graphene_point_t *p,
+                     float                  *w,
+                     int                     l,
+                     float                   t,
+                     graphene_point_t       *q)
+{
+  if (l == 1)
+    {
+      q->x = p[0].x;
+      q->y = p[0].y;
+    }
+  else
+    {
+      graphene_point_t *np;
+      float *nw;
+      int i;
+
+      np = g_alloca (sizeof (graphene_point3d_t) * (l - 1));
+      nw = g_alloca (sizeof (float) * (l - 1));
+
+      for (i = 0; i < l - 1; i++)
+        {
+          nw[i] = (1 - t) * w[i] + t * w[i + 1];
+          np[i].x = (1 - t) * (w[i]/nw[i]) * p[i].x + t * (w[i + 1]/nw[i]) * p[i + 1].x;
+          np[i].y = (1 - t) * (w[i]/nw[i]) * p[i].y + t * (w[i + 1]/nw[i]) * p[i + 1].y;
+        }
+      get_rational_bezier (np, nw, l - 1, t, q);
+    }
+}
+
+static void
+get_conic (const graphene_point_t points[3],
+           float                  weight,
+           float                  t,
+           graphene_point_t      *p)
+{
+  get_rational_bezier (points, (float [3]) { 1, weight, 1}, 3, t, p);
+}
+
+static gboolean
+acceptable (float t)
+{
+  return 0 <= t && t <= 1;
+}
+
+static int
+get_conic_extrema (float a, float b, float c, float w, float t[10])
+{
+  float q, tt;
+  int n = 0;
+
+  if ((w - 1)*(a - c) != 0)
+    {
+      q = - sqrt (a*a - 4*a*b*w*w + 4*a*c*w*w - 2*a*c + 4*b*b*w*w - 4*b*c*w*w + c*c);
+
+      tt = (- q + 2*a*w - a - 2*b*w + c)/(2*(w - 1)*(a - c));
+
+      if (acceptable (tt))
+        t[n++] = tt;
+
+      tt = (q + 2*a*w - a - 2*b*w + c)/(2*(w - 1)*(a - c));
+
+      if (acceptable (tt))
+        t[n++] = tt;
+    }
+
+  if (w * (b - c) != 0 && a == c)
+    t[n++] = 0.5;
+
+  if (w == 1 && a - 2*b + c != 0)
+    {
+      tt = (a - b) / (a - 2*b + c);
+      if (acceptable (tt))
+        t[n++] = tt;
+    }
+
+  return n;
+}
+
+static void
+get_conic_bounds (const graphene_point_t  p[3],
+                  float                   w,
+                  graphene_rect_t        *bounds)
+{
+  graphene_point_t q;
+  float t[10];
+  int i, n;
+
+  graphene_rect_init (bounds, p[0].x, p[0].y, 0, 0);
+  graphene_rect_expand (bounds, &p[2], bounds);
+
+  n = get_conic_extrema (p[0].x, p[1].x, p[2].x, w, t);
+  n += get_conic_extrema (p[0].y, p[1].y, p[2].y, w, &t[n]);
+
+  for (i = 0; i < n; i++)
+    {
+      get_conic (p, w, t[i], &q);
+      graphene_rect_expand (bounds, &q, bounds);
+    }
+}
+
+
+static void
+split_bezier3d_recurse (const graphene_point3d_t *p,
+                        int                       l,
+                        float                     t,
+                        graphene_point3d_t       *left,
+                        graphene_point3d_t       *right,
+                        int                      *lpos,
+                        int                      *rpos)
+{
+  if (l == 1)
+    {
+      left[*lpos] = p[0];
+      right[*rpos] = p[0];
+    }
+  else
+    {
+      graphene_point3d_t *np;
+      int i;
+
+      np = g_alloca (sizeof (graphene_point3d_t) * (l - 1));
+      for (i = 0; i < l - 1; i++)
+        {
+          if (i == 0)
+            {
+              left[*lpos] = p[i];
+              (*lpos)++;
+            }
+          if (i + 1 == l - 1)
+            {
+              right[*rpos] = p[i + 1];
+              (*rpos)--;
+            }
+          graphene_point3d_interpolate (&p[i], &p[i + 1], t, &np[i]);
+        }
+      split_bezier3d_recurse (np, l - 1, t, left, right, lpos, rpos);
+    }
+}
+
+static void
+split_bezier3d (const graphene_point3d_t *p,
+                int                       l,
+                float                     t,
+                graphene_point3d_t       *left,
+                graphene_point3d_t       *right)
+{
+  int lpos = 0;
+  int rpos = l - 1;
+  split_bezier3d_recurse (p, l, t, left, right, &lpos, &rpos);
+}
+
+static void
+split_conic (const graphene_point_t points[3],
+             float                  weight,
+             float                  t,
+             graphene_point_t       lp[3],
+             graphene_point_t       rp[3],
+             float                 *lw,
+             float                 *rw)
+{
+  graphene_point3d_t p[3];
+  graphene_point3d_t left[3], right[3];
+  int i;
+
+  /* do de Casteljau in homogeneous coordinates... */
+  for (i = 0; i < 3; i++)
+    {
+      p[i].x = points[i].x;
+      p[i].y = points[i].y;
+      p[i].z = 1;
+    }
+
+  p[1].x *= weight;
+  p[1].y *= weight;
+  p[1].z *= weight;
+
+  split_bezier3d (p, 3, t, left, right);
+
+  /* then project the control points down */
+  for (i = 0; i < 3; i++)
+    {
+      lp[i].x = left[i].x / left[i].z;
+      lp[i].y = left[i].y / left[i].z;
+      rp[i].x = right[i].x / right[i].z;
+      rp[i].y = right[i].y / right[i].z;
+    }
+
+  /* normalize the outer weights to be 1 by using
+   * the fact that weights w_i and c*w_i are equivalent
+   * for any nonzero constant c
+   */
+  for (i = 0; i < 3; i++)
+    {
+      left[i].z /= left[0].z;
+      right[i].z /= right[2].z;
+    }
+
+  /* normalize the inner weight to be 1 by using
+   * the fact that w_0*w_2/w_1^2 is a constant for
+   * all equivalent weights.
+   */
+  *lw = left[1].z / sqrt (left[2].z);
+  *rw = right[1].z / sqrt (right[0].z);
+}
+
+static void
+conic_intersection_recurse (Conic            *c1,
+                            Conic            *c2,
+                            float             t1,
+                            float             t2,
+                            float             s1,
+                            float             s2,
+                            float            *t,
+                            float            *s,
+                            graphene_point_t *p,
+                            int               n,
+                            int              *pos)
+{
+  graphene_rect_t b1, b2;
+  float d;
+  float e;
+
+  Conic c11, c12, c21, c22;
+
+  if (*pos == n)
+    return;
+
+  get_conic_bounds (c1->p, c1->w, &b1);
+  get_conic_bounds (c2->p, c2->w, &b2);
+
+  if (!graphene_rect_intersection (&b1, &b2, NULL))
+    return;
+
+  d = (t2 - t1) / 2;
+  e = (s2 - s1) / 2;
+
+  if (b1.size.width < 0.1 && b1.size.height < 0.1 &&
+      b2.size.width < 0.1 && b2.size.height < 0.1)
+    {
+      t[*pos] = t1 + d;
+      s[*pos] = s1 + e;
+      get_conic (c1->p, c1->w, 0.5, &p[*pos]);
+      (*pos)++;
+      return;
+    }
+
+  split_conic (c1->p, c1->w, 0.5, c11.p, c12.p, &c11.w, &c12.w);
+  split_conic (c2->p, c2->w, 0.5, c21.p, c22.p, &c21.w, &c22.w);
+
+  conic_intersection_recurse (&c11, &c21, t1, t1 + d, s1, s1 + e, t, s, p, n, pos);
+  conic_intersection_recurse (&c11, &c22, t1, t1 + d, s1 + e, s2, t, s, p, n, pos);
+  conic_intersection_recurse (&c12, &c21, t1 + d, t2, s1, s1 + e, t, s, p, n, pos);
+  conic_intersection_recurse (&c12, &c22, t1 + d, t2, s1 + e, s2, t, s, p, n, pos);
+}
+
+static int
+conic_intersection (Conic            *a,
+                    Conic            *b,
+                    float            *t,
+                    float            *s,
+                    graphene_point_t *p,
+                    int               n)
+{
+  int pos = 0;
+
+  conic_intersection_recurse (a, b, 0, 1, 0, 1, t, s, p, n, &pos);
+
+  return pos;
+}
+
 static void
 demo_widget_snapshot (GtkWidget   *widget,
                       GtkSnapshot *snapshot)
@@ -115,6 +431,56 @@ demo_widget_snapshot (GtkWidget   *widget,
   width = gtk_widget_get_width (widget);
   height = gtk_widget_get_width (widget);
 
+#if 1
+  {
+    Conic data;
+    Conic data2;
+    graphene_rect_t bounds;
+    float t[4], s[4];
+    graphene_point_t q[4];
+    int n, i;
+
+    data.w = 0;
+    data2.w = 1;
+    gsk_path_foreach (self->orig_path, GSK_PATH_FOREACH_ALLOW_CONIC,
+                      get_conic_segment, &data);
+    gsk_path_foreach (self->orig_path, GSK_PATH_FOREACH_ALLOW_CONIC,
+                      get_conic_segment, &data2);
+
+    n = conic_intersection (&data, &data2, t, s, q, 4);
+
+    builder = gsk_path_builder_new ();
+    for (i = 0; i < n; i++)
+      gsk_path_builder_add_circle (builder, &q[i], 5);
+
+    gsk_path_builder_move_to (builder, data.p[0].x, data.p[0].y);
+    gsk_path_builder_conic_to (builder,
+                               data.p[1].x, data.p[1].y,
+                               data.p[2].x, data.p[2].y,
+                               data.w);
+
+    gsk_path_builder_move_to (builder, data2.p[0].x, data2.p[0].y);
+    gsk_path_builder_conic_to (builder,
+                               data2.p[1].x, data2.p[1].y,
+                               data2.p[2].x, data2.p[2].y,
+                               data2.w);
+
+    path = gsk_path_builder_free_to_path (builder);
+
+    stroke = gsk_stroke_new (1);
+    gtk_snapshot_push_stroke (snapshot, path, stroke);
+    gsk_stroke_free (stroke);
+
+    gtk_snapshot_append_color (snapshot,
+                               &(GdkRGBA){ 0, 0, 0, 1},
+                               &GRAPHENE_RECT_INIT (0, 0, width, height ));
+
+    gtk_snapshot_pop (snapshot);
+
+    return;
+  }
+#endif
+
   if (self->do_stroke)
     {
       if (self->inside)
@@ -237,6 +603,7 @@ demo_widget_snapshot (GtkWidget   *widget,
 
       gtk_widget_snapshot_child (widget, self->label, snapshot);
     }
+
 }
 
 static void


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