Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gnome/testsuite/gsk/   (Gnome Linux Desktop Version 4.23.2©)  Datei vom 30.5.2026 mit Größe 12 kB image not shown  

Quelle  curve.c

  Sprache: C
 

#include <gtk/gtk.h>
#include "gsk/gskcurveprivate.h"

static void
init_random_point (graphene_point_t *p)
{
  p->x = g_test_rand_double_range (01000);
  p->y = g_test_rand_double_range (01000);
}

static void
init_random_curve_with_op (GskCurve         *curve,
                           GskPathOperation  min_op,
                           GskPathOperation  max_op)
{
  switch (g_test_rand_int_range (min_op, max_op + 1))
    {
    case GSK_PATH_LINE:
      {
        GskAlignedPoint p[2];

        init_random_point (&p[0].pt);
        init_random_point (&p[1].pt);
        gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_LINE, p));
      }
      break;

    case GSK_PATH_QUAD:
      {
        GskAlignedPoint p[3];

        init_random_point (&p[0].pt);
        init_random_point (&p[1].pt);
        init_random_point (&p[2].pt);
        gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_QUAD, p));
      }
    break;

    case GSK_PATH_CUBIC:
      {
        GskAlignedPoint p[4];

        init_random_point (&p[0].pt);
        init_random_point (&p[1].pt);
        init_random_point (&p[2].pt);
        init_random_point (&p[3].pt);
        gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CUBIC, p));
      }
    break;

    case GSK_PATH_CONIC:
      {
        GskAlignedPoint p[4];

        init_random_point (&p[0].pt);
        init_random_point (&p[1].pt);
        p[2].pt.x = g_test_rand_double_range (0.220);
        p[2].pt.y = 0.f;
        init_random_point (&p[3].pt);
        gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CONIC, p));
      }
    break;

    default:
      g_assert_not_reached ();
    }
}

static void
init_random_curve (GskCurve *curve)
{
  init_random_curve_with_op (curve, GSK_PATH_LINE, GSK_PATH_CONIC);
}

static void
test_curve_tangents (void)
{
  for (int i = 0; i < 100; i++)
    {
      GskCurve c;
      graphene_vec2_t vec, exact;

      init_random_curve (&c);

      gsk_curve_get_tangent (&c, 0, &vec);
      g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&vec), 1.0f, 0.00001);
      gsk_curve_get_start_tangent (&c, &exact);
      g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&exact), 1.0f, 0.00001);
      g_assert_true (graphene_vec2_near (&vec, &exact, 0.05));

      gsk_curve_get_tangent (&c, 1, &vec);
      g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&vec), 1.0f, 0.00001);
      gsk_curve_get_end_tangent (&c, &exact);
      g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&exact), 1.0f, 0.00001);
      g_assert_true (graphene_vec2_near (&vec, &exact, 0.05));
    }
}

static void
test_curve_points (void)
{
  for (int i = 0; i < 100; i++)
    {
      GskCurve c;
      graphene_point_t p;

      init_random_curve (&c);

      /* We could assert equality here because evaluating the polynomials with 0
       * has no effect on accuracy, but for arcs, we use trigonometric functions,
       * so allow a small error.
       */

      gsk_curve_get_point (&c, 0, &p);
      g_assert_true (graphene_point_near (gsk_curve_get_start_point (&c), &p, 0.001));

      /* But here we evaluate the polynomials with 1 which gives the highest possible
       * accuracy error. So we'll just be generous here.
       */

      gsk_curve_get_point (&c, 1, &p);
      g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c), &p, 0.05));
    }
}

/* at this point the subdivision stops and the decomposer
 * violates tolerance rules
 */

#define MIN_PROGRESS (1/1024.f)

typedef struct
{
  graphene_point_t p;
  float t;
} PointOnLine;

static gboolean
add_line_to_array (const graphene_point_t *from,
                   const graphene_point_t *to,
                   float                   from_progress,
                   float                   to_progress,
                   GskCurveLineReason      reason,
                   gpointer                user_data)
{
  GArray *array = user_data;
  PointOnLine *last = &g_array_index (array, PointOnLine, array->len - 1);

  g_assert_true (array->len > 0);
  g_assert_cmpfloat (from_progress, >=, 0.0f);
  g_assert_cmpfloat (from_progress, <, to_progress);
  g_assert_cmpfloat (to_progress, <=, 1.0f);

  g_assert_true (graphene_point_equal (&last->p, from));
  g_assert_cmpfloat (last->t, ==, from_progress);

  g_array_append_vals (array, (PointOnLine[1]) { { *to, to_progress } }, 1);

  return TRUE;
}

static void
test_curve_decompose (void)
{
  static const float tolerance = 0.5;

  for (int i = 0; i < 100; i++)
    {
      GArray *array;
      GskCurve c;

      init_random_curve (&c);

      array = g_array_new (FALSEFALSEsizeof (PointOnLine));
      g_array_append_vals (array, (PointOnLine[1]) { { *gsk_curve_get_start_point (&c), 0.f } }, 1);

      g_assert_true (gsk_curve_decompose (&c, tolerance, add_line_to_array, array));

      g_assert_cmpint (array->len, >=, 2); /* We at least got a line to the end */
      g_assert_cmpfloat (g_array_index (array, PointOnLine, array->len - 1).t, ==, 1.0);

      for (int j = 0; j < array->len; j++)
        {
          PointOnLine *pol = &g_array_index (array, PointOnLine, j);
          graphene_point_t p;

          /* Check that the points we got are actually on the line */
          gsk_curve_get_point (&c, pol->t, &p);
          g_assert_true (graphene_point_near (&pol->p, &p, 0.05));

          /* Check that the mid point is not further than the tolerance */
          if (j > 0)
            {
              PointOnLine *last = &g_array_index (array, PointOnLine, j - 1);
              graphene_point_t mid;

              if (pol->t - last->t > MIN_PROGRESS)
                {
                  graphene_point_interpolate (&last->p, &pol->p, 0.5, &mid);
                  gsk_curve_get_point (&c, (pol->t + last->t) / 2, &p);
                  /* The decomposer does this cheaper Manhattan distance test,
                   * so graphene_point_near() does not work */

                  g_assert_cmpfloat (fabs (mid.x - p.x), <=, tolerance + 0.0002);
                  g_assert_cmpfloat (fabs (mid.y - p.y), <=, tolerance + 0.0002);
                }
            }
        }

      g_array_unref (array);
    }
}

static gboolean
add_curve_to_array (GskPathOperation        op,
                    const graphene_point_t *pts,
                    gsize                   n_pts,
                    float                   weight,
                    gpointer                user_data)
{
  GArray *array = user_data;
  GskCurve c;

  gsk_curve_init_foreach (&c, op, pts, n_pts, weight);
  g_array_append_val (array, c);

  return TRUE;
}

static void
test_curve_decompose_into (GskPathForeachFlags flags)
{
  for (int i = 0; i < 100; i++)
    {
      GskCurve c;
      GskPathBuilder *builder;
      const graphene_point_t *s;
      GskPath *path;
      GArray *array;

      init_random_curve (&c);

      builder = gsk_path_builder_new ();

      s = gsk_curve_get_start_point (&c);
      gsk_path_builder_move_to (builder, s->x, s->y);
      gsk_curve_builder_to (&c, builder);
      path = gsk_path_builder_free_to_path (builder);

      array = g_array_new (FALSEFALSEsizeof (GskCurve));

      g_assert_true (gsk_curve_decompose_curve (&c, flags, 0.1, add_curve_to_array, array));

      g_assert_cmpint (array->len, >=, 1);

      for (int j = 0; j < array->len; j++)
        {
          GskCurve *c2 = &g_array_index (array, GskCurve, j);

          switch (c2->op)
            {
            case GSK_PATH_MOVE:
            case GSK_PATH_CLOSE:
            case GSK_PATH_LINE:
              break;
            case GSK_PATH_QUAD:
              g_assert_true (flags & GSK_PATH_FOREACH_ALLOW_QUAD);
              break;
            case GSK_PATH_CUBIC:
              g_assert_true (flags & GSK_PATH_FOREACH_ALLOW_CUBIC);
              break;
            case GSK_PATH_CONIC:
              g_assert_true (flags & GSK_PATH_FOREACH_ALLOW_CONIC);
              break;
            default:
              g_assert_not_reached ();
            }
        }

      g_array_unref (array);
      gsk_path_unref (path);
    }
}

static void
test_curve_decompose_into_line (void)
{
  test_curve_decompose_into (0);
}

static void
test_curve_decompose_into_quad (void)
{
  test_curve_decompose_into (GSK_PATH_FOREACH_ALLOW_QUAD);
}

static void
test_curve_decompose_into_cubic (void)
{
  test_curve_decompose_into (GSK_PATH_FOREACH_ALLOW_CUBIC);
}

/* Some sanity checks for splitting curves. */
static void
test_curve_split (void)
{
  for (int i = 0; i < 20; i++)
    {
      GskCurve c;

      init_random_curve (&c);

      for (int j = 0; j < 20; j++)
        {
          GskCurve c1, c2;
          graphene_point_t p;
          graphene_vec2_t t, t1, t2;
          float split;

          split = g_test_rand_double_range (0.10.9);

          gsk_curve_split (&c, split, &c1, &c2);

          g_assert_true (c1.op == c.op);
          g_assert_true (c2.op == c.op);

          g_assert_true (graphene_point_near (gsk_curve_get_start_point (&c),
                                              gsk_curve_get_start_point (&c1), 0.005));
          g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c1),
                                              gsk_curve_get_start_point (&c2), 0.005));
          g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c),
                                              gsk_curve_get_end_point (&c2), 0.005));
          gsk_curve_get_point (&c, split, &p);
          gsk_curve_get_tangent (&c, split, &t);
          g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c1), &p, 0.005));
          g_assert_true (graphene_point_near (gsk_curve_get_start_point (&c2), &p, 0.005));

          gsk_curve_get_start_tangent (&c, &t1);
          gsk_curve_get_start_tangent (&c1, &t2);
          g_assert_true (graphene_vec2_near (&t1, &t2, 0.005));
          gsk_curve_get_end_tangent (&c1, &t1);
          gsk_curve_get_start_tangent (&c2, &t2);
          g_assert_true (graphene_vec2_near (&t1, &t2, 0.005));
          g_assert_true (graphene_vec2_near (&t, &t1, 0.005));
          g_assert_true (graphene_vec2_near (&t, &t2, 0.005));
          gsk_curve_get_end_tangent (&c, &t1);
          gsk_curve_get_end_tangent (&c2, &t2);
          g_assert_true (graphene_vec2_near (&t1, &t2, 0.005));

#if 0
          /* hard to guarantee this for totally random random curves */
          g_assert_cmpfloat_with_epsilon (gsk_curve_get_length (&c),
                                          gsk_curve_get_length (&c1) + gsk_curve_get_length (&c2),
                                          1);
#endif
        }
    }
}

static void
test_curve_derivative (void)
{
  GskCurve c;
  float t;
  graphene_vec2_t t1, t2;
  graphene_point_t p;

  for (int i = 0; i < 100; i++)
    {
      init_random_curve (&c);

      for (int j = 0; j < 100; j++)
        {
          t = g_test_rand_double_range (01);
          gsk_curve_get_derivative_at (&c, t, &p);
          gsk_curve_get_tangent (&c, t, &t1);
          graphene_vec2_init (&t2, p.x, p.y);
          graphene_vec2_normalize (&t2, &t2);
          g_assert_true (graphene_vec2_near (&t1, &t2, 0.1));
        }
    }
}

static void
test_curve_length (void)
{
  GskCurve c;
  float l, l0;

  for (int i = 0; i < 1000; i++)
    {
      init_random_curve (&c);

      l = gsk_curve_get_length (&c);
      l0 = graphene_point_distance (gsk_curve_get_start_point (&c),
                                    gsk_curve_get_end_point (&c),
                                    NULL, NULL);
      g_assert_true (l >= l0 - 0.001);
      if (c.op == GSK_PATH_LINE)
        g_assert_cmpfloat_with_epsilon (l, l0, 0.001);
    }
}

int
main (int argc, char *argv[])
{
  (g_test_init) (&argc, &argv, NULL);

  g_test_add_func ("/curve/points", test_curve_points);
  g_test_add_func ("/curve/tangents", test_curve_tangents);
  g_test_add_func ("/curve/decompose", test_curve_decompose);
  g_test_add_func ("/curve/decompose-line", test_curve_decompose_into_line);
  g_test_add_func ("/curve/decompose-quad", test_curve_decompose_into_quad);
  g_test_add_func ("/curve/decompose-cubic", test_curve_decompose_into_cubic);
  g_test_add_func ("/curve/split", test_curve_split);
  g_test_add_func ("/curve/derivative", test_curve_derivative);
  g_test_add_func ("/curve/length", test_curve_length);

  return g_test_run ();
}

Messung V0.5 in Prozent
C=98 H=91 G=94

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet am  2026-07-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.