[gtk/wip/matthiasc/lottie-stroke] wip: conic math debug
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/matthiasc/lottie-stroke] wip: conic math debug
- Date: Thu, 3 Dec 2020 12:46:20 +0000 (UTC)
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]