This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch edje-vector-intergration
in repository efl.

View the commit online.

commit b660205cbc22a4efbb7504565d7dcadb2a5e6aee
Author: [email protected] <[email protected]>
AuthorDate: Wed Apr 29 10:10:11 2026 -0600

    edje: add per-property lerp helpers (Task 5.1 Phase 5)
    
    Implement five componentwise lerp functions to serve as substrate for
    Phase 5's name-aligned tree walker (_edje_vg_tree_lerp). Each helper
    handles one property family from the vector state animation spec §5.2:
    
    - _edje_vg_color_lerp: RGBA interpolation with rounding; color_class
      field is stepped (borrowed pointer) since binding-name doesn't
      participate in lerp, only the resolved color does. This becomes
      relevant in Phase 6 when materializer re-applies bindings.
    
    - _edje_vg_xform_lerp: Handles both decomposed (tx, ty, sx, sy, angle)
      and matrix-form (3x3 row-major) representations. When either side is
      matrix, both are normalized to matrix space via T·R·S composition
      and lerped element-wise. Otherwise decomposed lerp uses shortest-arc
      rotation (delta normalized to [-180, 180]) to prevent wrap-around
      through 180°. Uses no premultiplication (matrix form inherently
      non-premul due to affine structure).
    
    - _edje_vg_stops_lerp / _edje_vg_dash_lerp: Return false on count
      mismatch, leaving step/hold semantics to the caller. Per-element
      lerp on match.
    
    - _edje_vg_viewbox_lerp: Componentwise on 4 doubles with NULL guards.
    
    These helpers currently have no consumers; Task 5.2 will call them
    from _edje_vg_tree_lerp to build the interpolated property tree.
    
    Includes 8 unit tests covering endpoints (pos=0/1), midpoints, shortest-
    arc edge case, count-mismatch paths, and viewbox composition.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
 src/lib/edje/edje_vg_tree.c              | 130 ++++++++++++++++
 src/lib/edje/edje_vg_tree.h              |  40 +++++
 src/tests/edje/edje_test_vector_states.c | 253 +++++++++++++++++++++++++++++++
 3 files changed, 423 insertions(+)

diff --git a/src/lib/edje/edje_vg_tree.c b/src/lib/edje/edje_vg_tree.c
index 9ee8850d57..0b1fc738a1 100644
--- a/src/lib/edje/edje_vg_tree.c
+++ b/src/lib/edje/edje_vg_tree.c
@@ -967,6 +967,136 @@ _edje_vg_override_free(Edje_Vg_Override *ovr)
    free(ovr);
 }
 
+/* =========================================================================
+ * Phase 5.1 — Per-property lerp helpers
+ * ========================================================================= */
+
+/* Helper: materialise a decomposed transform to a row-major 3×3 matrix.
+ * Composition order: T · R · S.
+ *
+ *   [ sx*c   -sy*s   tx ]
+ *   [ sx*s    sy*c   ty ]
+ *   [ 0       0       1 ]
+ *
+ * where c = cos(angle_rad), s = sin(angle_rad). */
+static void
+_decomposed_to_matrix(const Edje_Vg_Transform *t, double m[9])
+{
+   double rad = t->angle * M_PI / 180.0;
+   double c = cos(rad), s = sin(rad);
+   m[0] = t->sx * c;   m[1] = -t->sy * s;  m[2] = t->tx;
+   m[3] = t->sx * s;   m[4] =  t->sy * c;  m[5] = t->ty;
+   m[6] = 0.0;         m[7] = 0.0;          m[8] = 1.0;
+}
+
+EAPI void
+_edje_vg_color_lerp(Edje_Vg_Color_Binding *out,
+                    const Edje_Vg_Color_Binding *a,
+                    const Edje_Vg_Color_Binding *b,
+                    double pos)
+{
+   double q;
+   if (!out || !a || !b) return;
+   q = 1.0 - pos;
+   out->r = (unsigned char)(q * a->r + pos * b->r + 0.5);
+   out->g = (unsigned char)(q * a->g + pos * b->g + 0.5);
+   out->b = (unsigned char)(q * a->b + pos * b->b + 0.5);
+   out->a = (unsigned char)(q * a->a + pos * b->a + 0.5);
+   /* color_class is stepped: use a's name for the first half, b's after. */
+   out->color_class = (pos < 0.5) ? a->color_class : b->color_class;
+}
+
+EAPI void
+_edje_vg_xform_lerp(Edje_Vg_Transform *out,
+                    const Edje_Vg_Transform *a,
+                    const Edje_Vg_Transform *b,
+                    double pos)
+{
+   double q;
+   if (!out || !a || !b) return;
+   q = 1.0 - pos;
+
+   if (a->has_matrix || b->has_matrix)
+     {
+        /* At least one side is matrix form — lerp element-wise in matrix space.
+         * Materialise the decomposed side using T·R·S composition. */
+        const double *am = a->m;
+        const double *bm = b->m;
+        double a_mat[9], b_mat[9];
+        int i;
+        if (!a->has_matrix) { _decomposed_to_matrix(a, a_mat); am = a_mat; }
+        if (!b->has_matrix) { _decomposed_to_matrix(b, b_mat); bm = b_mat; }
+        out->has_matrix = 1;
+        for (i = 0; i < 9; i++)
+          out->m[i] = q * am[i] + pos * bm[i];
+     }
+   else
+     {
+        double delta;
+        out->has_matrix = 0;
+        out->tx = q * a->tx + pos * b->tx;
+        out->ty = q * a->ty + pos * b->ty;
+        out->sx = q * a->sx + pos * b->sx;
+        out->sy = q * a->sy + pos * b->sy;
+        /* Shortest signed arc: normalise delta to [-180, 180]. */
+        delta = b->angle - a->angle;
+        while (delta >  180.0) delta -= 360.0;
+        while (delta < -180.0) delta += 360.0;
+        out->angle = a->angle + pos * delta;
+     }
+}
+
+EAPI Eina_Bool
+_edje_vg_stops_lerp(Edje_Vg_Stop *out, unsigned int out_count,
+                    const Edje_Vg_Stop *a, unsigned int a_count,
+                    const Edje_Vg_Stop *b, unsigned int b_count,
+                    double pos)
+{
+   double q;
+   unsigned int i;
+   if (a_count != b_count) return EINA_FALSE;
+   if (out_count != a_count) return EINA_FALSE;
+   if (!out || !a || !b) return EINA_FALSE;
+   q = 1.0 - pos;
+   for (i = 0; i < a_count; i++)
+     {
+        out[i].offset = q * a[i].offset + pos * b[i].offset;
+        _edje_vg_color_lerp(&out[i].color, &a[i].color, &b[i].color, pos);
+     }
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+_edje_vg_dash_lerp(double *out, unsigned int out_count,
+                   const double *a, unsigned int a_count,
+                   const double *b, unsigned int b_count,
+                   double pos)
+{
+   double q;
+   unsigned int i;
+   if (a_count != b_count) return EINA_FALSE;
+   if (out_count != a_count) return EINA_FALSE;
+   if (!out || !a || !b) return EINA_FALSE;
+   q = 1.0 - pos;
+   for (i = 0; i < a_count; i++)
+     out[i] = q * a[i] + pos * b[i];
+   return EINA_TRUE;
+}
+
+EAPI void
+_edje_vg_viewbox_lerp(double *out_x, double *out_y,
+                      double *out_w, double *out_h,
+                      double ax, double ay, double aw, double ah,
+                      double bx, double by, double bw, double bh,
+                      double pos)
+{
+   double q = 1.0 - pos;
+   if (out_x) *out_x = q * ax + pos * bx;
+   if (out_y) *out_y = q * ay + pos * by;
+   if (out_w) *out_w = q * aw + pos * bw;
+   if (out_h) *out_h = q * ah + pos * bh;
+}
+
 /* =========================================================================
  * Phase 3.1 — Materializer: Edje_Vg_Tree → Efl_VG object tree
  *
diff --git a/src/lib/edje/edje_vg_tree.h b/src/lib/edje/edje_vg_tree.h
index 3e30a199c3..f8d799281b 100644
--- a/src/lib/edje/edje_vg_tree.h
+++ b/src/lib/edje/edje_vg_tree.h
@@ -97,6 +97,46 @@ EAPI Edje_Vg_Override *_edje_vg_override_dup(const Edje_Vg_Override *src);
  * Safe to call with NULL. */
 EAPI void              _edje_vg_override_free(Edje_Vg_Override *ovr);
 
+/* --- Phase 5.1: Per-property lerp helpers --------------------------------- */
+
+/* Componentwise RGBA lerp on Edje_Vg_Color_Binding (raw RGBA, no premul;
+ * class resolution arrives in Phase 6 BEFORE the lerp).  color_class field
+ * is stepped: a's at pos < 0.5, else b's.  Strings are not ref-counted in
+ * the output (caller copies if persisting). */
+EAPI void _edje_vg_color_lerp(Edje_Vg_Color_Binding *out,
+                               const Edje_Vg_Color_Binding *a,
+                               const Edje_Vg_Color_Binding *b,
+                               double pos);
+
+/* Decomposed transform: linear lerp on tx/ty/sx/sy; rotate via shortest
+ * signed arc.  If either side has_matrix, lerp in matrix space (materialise
+ * the decomposed side via T·R·S composition first). */
+EAPI void _edje_vg_xform_lerp(Edje_Vg_Transform *out,
+                               const Edje_Vg_Transform *a,
+                               const Edje_Vg_Transform *b,
+                               double pos);
+
+/* Per-stop lerp if counts match.  Returns EINA_TRUE if lerped, EINA_FALSE if
+ * counts differ (caller should step to a or b based on pos). */
+EAPI Eina_Bool _edje_vg_stops_lerp(Edje_Vg_Stop *out, unsigned int out_count,
+                                    const Edje_Vg_Stop *a, unsigned int a_count,
+                                    const Edje_Vg_Stop *b, unsigned int b_count,
+                                    double pos);
+
+/* Dash pattern lerp if pair counts match.  Returns EINA_TRUE if lerped,
+ * EINA_FALSE if counts differ. */
+EAPI Eina_Bool _edje_vg_dash_lerp(double *out, unsigned int out_count,
+                                   const double *a, unsigned int a_count,
+                                   const double *b, unsigned int b_count,
+                                   double pos);
+
+/* Componentwise viewbox lerp.  Each output pointer may be NULL (skipped). */
+EAPI void _edje_vg_viewbox_lerp(double *out_x, double *out_y,
+                                 double *out_w, double *out_h,
+                                 double ax, double ay, double aw, double ah,
+                                 double bx, double by, double bw, double bh,
+                                 double pos);
+
 /* --- Eet descriptor accessor -------------------------------------------- */
 
 /* Returns the static Eet_Data_Descriptor for Edje_Vg_Tree.
diff --git a/src/tests/edje/edje_test_vector_states.c b/src/tests/edje/edje_test_vector_states.c
index a7f3f5c913..670f5c1838 100644
--- a/src/tests/edje/edje_test_vector_states.c
+++ b/src/tests/edje/edje_test_vector_states.c
@@ -2,6 +2,7 @@
 # include <config.h>
 #endif
 
+#include <math.h>
 #include <Eina.h>
 #include <Eet.h>
 #include <Evas.h>
@@ -60,6 +61,56 @@ Edje_Vg_Tree         *_edje_vg_tree_dup(const Edje_Vg_Tree *t);
 void                  _edje_vg_tree_apply_override(Edje_Vg_Tree *working,
                                                    const void *ovr);
 
+/* Phase 5.1 lerp helpers — forward declarations.
+ *
+ * We redeclare the value-type structs used by these helpers because the test
+ * file cannot include edje_private.h.  The layouts below match edje_private.h
+ * exactly:
+ *
+ *   Edje_Vg_Color_Binding: unsigned char r,g,b,a; const char *color_class;
+ *   Edje_Vg_Stop:          double offset; Edje_Vg_Color_Binding color;
+ *   Edje_Vg_Transform:     double tx,ty,angle,sx,sy,m[9]; unsigned char has_matrix;
+ */
+typedef struct {
+   unsigned char  r, g, b, a;
+   const char    *color_class;
+} Edje_Vg_Color_Binding;
+
+typedef struct {
+   double                offset;
+   Edje_Vg_Color_Binding color;
+} Edje_Vg_Stop;
+
+typedef struct {
+   double        tx, ty;
+   double        angle;
+   double        sx, sy;
+   double        m[9];
+   unsigned char has_matrix;
+} Edje_Vg_Transform;
+
+void         _edje_vg_color_lerp(Edje_Vg_Color_Binding *out,
+                                 const Edje_Vg_Color_Binding *a,
+                                 const Edje_Vg_Color_Binding *b,
+                                 double pos);
+void         _edje_vg_xform_lerp(Edje_Vg_Transform *out,
+                                 const Edje_Vg_Transform *a,
+                                 const Edje_Vg_Transform *b,
+                                 double pos);
+Eina_Bool    _edje_vg_stops_lerp(Edje_Vg_Stop *out, unsigned int out_count,
+                                 const Edje_Vg_Stop *a, unsigned int a_count,
+                                 const Edje_Vg_Stop *b, unsigned int b_count,
+                                 double pos);
+Eina_Bool    _edje_vg_dash_lerp(double *out, unsigned int out_count,
+                                const double *a, unsigned int a_count,
+                                const double *b, unsigned int b_count,
+                                double pos);
+void         _edje_vg_viewbox_lerp(double *out_x, double *out_y,
+                                   double *out_w, double *out_h,
+                                   double ax, double ay, double aw, double ah,
+                                   double bx, double by, double bw, double bh,
+                                   double pos);
+
 /* Partial view of Edje_Vg_Override: all fields, inline payload shape sub-struct.
  * We replicate only the fields needed by the three Task 4.1 tests.
  * The payload is a full Edje_Vg_Node embedded (not a pointer), so we mirror
@@ -878,6 +929,199 @@ EFL_START_TEST(edje_vg_render_box_a_hover)
 }
 EFL_END_TEST
 
+/* =========================================================================
+ * Phase 5.1 — Lerp helper unit tests
+ * ========================================================================= */
+
+/*
+ * edje_vg_color_lerp_basic
+ *
+ * Verify that mid-lerp (pos=0.5) produces the correct rounded average RGBA.
+ */
+EFL_START_TEST(edje_vg_color_lerp_basic)
+{
+   Edje_Vg_Color_Binding a = { 100, 200, 50, 255, NULL };
+   Edje_Vg_Color_Binding b = { 200, 100, 150, 255, NULL };
+   Edje_Vg_Color_Binding out;
+
+   memset(&out, 0, sizeof(out));
+   _edje_vg_color_lerp(&out, &a, &b, 0.5);
+   ck_assert_int_eq(out.r, 150);
+   ck_assert_int_eq(out.g, 150);
+   ck_assert_int_eq(out.b, 100);
+   ck_assert_int_eq(out.a, 255);
+}
+EFL_END_TEST
+
+/*
+ * edje_vg_color_lerp_endpoints
+ *
+ * At pos=0 the output must equal a; at pos=1 it must equal b.
+ */
+EFL_START_TEST(edje_vg_color_lerp_endpoints)
+{
+   Edje_Vg_Color_Binding a = { 0, 0, 0, 255, NULL };
+   Edje_Vg_Color_Binding b = { 255, 255, 255, 255, NULL };
+   Edje_Vg_Color_Binding out;
+
+   memset(&out, 0, sizeof(out));
+   _edje_vg_color_lerp(&out, &a, &b, 0.0);
+   fail_if(out.r != 0 || out.g != 0 || out.b != 0);
+
+   _edje_vg_color_lerp(&out, &a, &b, 1.0);
+   fail_if(out.r != 255 || out.g != 255 || out.b != 255);
+}
+EFL_END_TEST
+
+/*
+ * edje_vg_xform_lerp_decomposed
+ *
+ * Lerp two decomposed transforms; verify tx/ty/sx/sy/angle mid-values.
+ */
+EFL_START_TEST(edje_vg_xform_lerp_decomposed)
+{
+   /* identity */
+   Edje_Vg_Transform a;
+   memset(&a, 0, sizeof(a));
+   a.sx = 1.0; a.sy = 1.0;
+   a.tx = 0.0; a.ty = 0.0;
+   a.angle = 0.0;
+   a.has_matrix = 0;
+
+   /* rotated + scaled + translated */
+   Edje_Vg_Transform b;
+   memset(&b, 0, sizeof(b));
+   b.sx = 2.0; b.sy = 2.0;
+   b.tx = 100.0; b.ty = 200.0;
+   b.angle = 90.0;
+   b.has_matrix = 0;
+
+   Edje_Vg_Transform out;
+   memset(&out, 0, sizeof(out));
+   _edje_vg_xform_lerp(&out, &a, &b, 0.5);
+
+   fail_if(out.tx != 50.0 || out.ty != 100.0);
+   fail_if(out.sx != 1.5 || out.sy != 1.5);
+   fail_if(out.angle < 44.9 || out.angle > 45.1);
+   fail_if(out.has_matrix);
+}
+EFL_END_TEST
+
+/*
+ * edje_vg_xform_lerp_shortest_arc
+ *
+ * 350° → 10° must lerp through 0° (shortest arc = 20°), not through 180°.
+ * Mid-point result should be near 0° (or 360°, equivalent).
+ */
+EFL_START_TEST(edje_vg_xform_lerp_shortest_arc)
+{
+   Edje_Vg_Transform a;
+   memset(&a, 0, sizeof(a));
+   a.sx = 1.0; a.sy = 1.0;
+   a.angle = 350.0;
+
+   Edje_Vg_Transform b;
+   memset(&b, 0, sizeof(b));
+   b.sx = 1.0; b.sy = 1.0;
+   b.angle = 10.0;
+
+   Edje_Vg_Transform out;
+   memset(&out, 0, sizeof(out));
+   _edje_vg_xform_lerp(&out, &a, &b, 0.5);
+
+   /* Normalise to [0, 360) and verify we're near 0° (not near 180°). */
+   double angle_norm = fmod(out.angle + 720.0, 360.0);
+   fail_if(angle_norm > 1.0 && angle_norm < 359.0);
+}
+EFL_END_TEST
+
+/*
+ * edje_vg_stops_lerp_count_mismatch_steps
+ *
+ * Count mismatch (2 vs 3) must return EINA_FALSE (step semantics).
+ */
+EFL_START_TEST(edje_vg_stops_lerp_count_mismatch_steps)
+{
+   Edje_Vg_Stop a[2];
+   Edje_Vg_Stop b[3];
+   Edje_Vg_Stop out[2];
+   Eina_Bool lerped;
+
+   memset(a, 0, sizeof(a));
+   memset(b, 0, sizeof(b));
+   memset(out, 0, sizeof(out));
+
+   lerped = _edje_vg_stops_lerp(out, 2, a, 2, b, 3, 0.5);
+   fail_if(lerped);
+}
+EFL_END_TEST
+
+/*
+ * edje_vg_stops_lerp_count_match
+ *
+ * When counts match the function must lerp offsets and colors, returning true.
+ */
+EFL_START_TEST(edje_vg_stops_lerp_count_match)
+{
+   Edje_Vg_Stop a[2] = {
+      { 0.0, { 0,   0,   0,   255, NULL } },
+      { 1.0, { 255, 0,   0,   255, NULL } },
+   };
+   Edje_Vg_Stop b[2] = {
+      { 0.0, { 255, 255, 255, 255, NULL } },
+      { 0.5, { 0,   0,   255, 255, NULL } },
+   };
+   Edje_Vg_Stop out[2];
+   Eina_Bool lerped;
+
+   memset(out, 0, sizeof(out));
+   lerped = _edje_vg_stops_lerp(out, 2, a, 2, b, 2, 0.5);
+   fail_if(!lerped);
+
+   /* stop 0: offset 0→0 = 0; color (0,0,0,255)→(255,255,255,255) at 0.5.
+    * 0.5*255 = 127.5 rounds to 128 with +0.5 integer rounding. */
+   ck_assert_int_eq(out[0].color.r, 128);
+
+   /* stop 1: offset 1.0→0.5 at 0.5 = 0.75 */
+   fail_if(out[1].offset < 0.74 || out[1].offset > 0.76);
+}
+EFL_END_TEST
+
+/*
+ * edje_vg_dash_lerp_match
+ *
+ * Dash arrays of equal count must be lerped componentwise.
+ */
+EFL_START_TEST(edje_vg_dash_lerp_match)
+{
+   double a[4]   = { 5.0, 3.0, 2.0, 2.0 };
+   double b[4]   = { 10.0, 6.0, 4.0, 4.0 };
+   double out[4];
+   Eina_Bool lerped;
+
+   memset(out, 0, sizeof(out));
+   lerped = _edje_vg_dash_lerp(out, 4, a, 4, b, 4, 0.5);
+   fail_if(!lerped);
+   fail_if(out[0] != 7.5 || out[2] != 3.0);
+}
+EFL_END_TEST
+
+/*
+ * edje_vg_viewbox_lerp_componentwise
+ *
+ * All four viewbox components lerped independently.
+ */
+EFL_START_TEST(edje_vg_viewbox_lerp_componentwise)
+{
+   double ox, oy, ow, oh;
+   _edje_vg_viewbox_lerp(&ox, &oy, &ow, &oh,
+                          0, 0, 100, 100,
+                          50, 50, 200, 200,
+                          0.5);
+   fail_if(ox != 25.0 || oy != 25.0 || ow != 150.0 || oh != 150.0);
+}
+EFL_END_TEST
+
 void
 edje_test_vector_states(TCase *tc)
 {
@@ -892,4 +1136,13 @@ edje_test_vector_states(TCase *tc)
    tcase_add_test(tc, edje_vg_tree_apply_override_visible);
    tcase_add_test(tc, edje_vg_tree_apply_override_unresolved_noop);
    tcase_add_test(tc, edje_vg_render_box_a_hover);
+   /* Phase 5.1 lerp helpers */
+   tcase_add_test(tc, edje_vg_color_lerp_basic);
+   tcase_add_test(tc, edje_vg_color_lerp_endpoints);
+   tcase_add_test(tc, edje_vg_xform_lerp_decomposed);
+   tcase_add_test(tc, edje_vg_xform_lerp_shortest_arc);
+   tcase_add_test(tc, edje_vg_stops_lerp_count_mismatch_steps);
+   tcase_add_test(tc, edje_vg_stops_lerp_count_match);
+   tcase_add_test(tc, edje_vg_dash_lerp_match);
+   tcase_add_test(tc, edje_vg_viewbox_lerp_componentwise);
 }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to