Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  path-special-cases.c

  Sprache: C
 

/*
 * Copyright © 2020 Benjamin Otte
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: Benjamin Otte <otte@gnome.org>
 */


#include <gtk/gtk.h>

/* testcases from path_parser.rs in librsvg */
static void
test_rsvg_parse (void)
{
  struct {
    const char *in;
    const char *out;
  } tests[] = {
    { """" },
    // numbers
    { "M 10 20""M 10 20" },
    { "M -10 -20""M -10 -20" },
    { "M .10 0.20""M 0.1 0.2" },
    { "M -.10 -0.20""M -0.1 -0.2" },
    { "M-.10-0.20""M -0.1 -0.2" },
    { "M10.5.50""M 10.5 0.5" },
    { "M.10.20""M 0.1 0.2" },
    { "M .10E1 .20e-4""M 1 2e-05" },
    { "M-.10E1-.20""M -1 -0.2" },
    { "M10.10E2 -0.20e3""M 1010 -200" },
    { "M-10.10E2-0.20e-3""M -1010 -0.0002" },
    { "M1e2.5""M 100 0.5" },
    { "M1e-2.5""M 0.01 0.5" },
    { "M1e+2.5""M 100 0.5" },
    // bogus numbers
    { "M+", NULL },
    { "M-", NULL },
    { "M+x", NULL },
    { "M10e", NULL },
    { "M10ex", NULL },
    { "M10e-", NULL },
    { "M10e+x", NULL },
    // numbers with comma
    { "M 10, 20""M 10 20" },
    { "M -10,-20""M -10 -20" },
    { "M.10    ,     0.20""M 0.1 0.2" },
    { "M -.10, -0.20   ""M -0.1 -0.2" },
    { "M-.10-0.20""M -0.1 -0.2" },
    { "M.10.20""M 0.1 0.2" },
    { "M .10E1,.20e-4""M 1 2e-05" },
    { "M-.10E-2,-.20""M -0.001 -0.2" },
    { "M10.10E2,-0.20e3""M 1010 -200" },
    { "M-10.10E2,-0.20e-3""M -1010 -0.0002" },
    // single moveto
    { "M 10 20 ""M 10 20" },
    { "M10,20  ""M 10 20" },
    { "M10 20   ""M 10 20" },
    { "    M10,20     ""M 10 20" },
    // relative moveto
    { "m10 20""M 10 20" },
    // absolute moveto with implicit lineto
    { "M10 20 30 40""M 10 20 L 30 40" },
    { "M10,20,30,40""M 10 20 L 30 40" },
    { "M.1-2,3E2-4""M 0.1 -2 L 300 -4" },
    // relative moveto with implicit lineto
    { "m10 20 30 40""M 10 20 L 40 60" },
    // relative moveto with relative lineto sequence
    { "m 46,447 l 0,0.5 -1,0 -1,0 0,1 0,12",
      "M 46 447 L 46 447.5 L 45 447.5 L 44 447.5 L 44 448.5 L 44 460.5" },
    // absolute moveto with implicit linetos
    { "M10,20 30,40,50 60""M 10 20 L 30 40 L 50 60" },
    // relative moveto with implicit linetos
    { "m10 20 30 40 50 60""M 10 20 L 40 60 L 90 120" },
    // absolute moveto moveto
    { "M10 20 M 30 40""M 10 20 M 30 40" },
    // relative moveto moveto
    { "m10 20 m 30 40""M 10 20 M 40 60" },
    // relative moveto lineto moveto
    { "m10 20 30 40 m 50 60""M 10 20 L 40 60 M 90 120" },
    // absolute moveto lineto
    { "M10 20 L30,40""M 10 20 L 30 40" },
    // relative moveto lineto
    { "m10 20 l30,40""M 10 20 L 40 60" },
    // relative moveto lineto lineto abs lineto
    { "m10 20 30 40l30,40,50 60L200,300",
      "M 10 20 L 40 60 L 70 100 L 120 160 L 200 300" },
    // horizontal lineto
    { "M10 20 H30""M 10 20 L 30 20" },
    { "M 10 20 H 30 40",  "M 10 20 L 30 20 L 40 20" },
    { "M10 20 H30,40-50""M 10 20 L 30 20 L 40 20 L -50 20" },
    { "m10 20 h30,40-50""M 10 20 L 40 20 L 80 20 L 30 20" },
    // vertical lineto
    { "M10 20 V30""M 10 20 L 10 30" },
    { "M10 20 V30 40""M 10 20 L 10 30 L 10 40" },
    { "M10 20 V30,40-50""M 10 20 L 10 30 L 10 40 L 10 -50" },
    { "m10 20 v30,40-50""M 10 20 L 10 50 L 10 90 L 10 40" },
    // curveto
    { "M10 20 C 30,40 50 60-70,80""M 10 20 C 30 40, 50 60, -70 80" },
    { "M10 20 C 30,40 50 60-70,80,90 100,110 120,130,140",
      "M 10 20 C 30 40, 50 60, -70 80 C 90 100, 110 120, 130 140" },
    { "m10 20 c 30,40 50 60-70,80,90 100,110 120,130,140",
      "M 10 20 C 40 60, 60 80, -60 100 C 30 200, 50 220, 70 240" },
    { "m10 20 c 30,40 50 60-70,80 90 100,110 120,130,140",
      "M 10 20 C 40 60, 60 80, -60 100 C 30 200, 50 220, 70 240" },
    // smooth curveto
    { "M10 20 S 30,40-50,60""M 10 20 C 10 20, 30 40, -50 60" },
    { "M10 20 S 30,40 50 60-70,80,90 100",
      "M 10 20 C 10 20, 30 40, 50 60 C 70 80, -70 80, 90 100" },
    // quadratic curveto
    { "M10 20 Q30 40 50 60""M 10 20 Q 30 40, 50 60" },
    { "M10 20 Q30 40 50 60,70,80-90 100",
      "M 10 20 Q 30 40, 50 60 Q 70 80, -90 100" },
    { "m10 20 q 30,40 50 60-70,80 90 100",
      "M 10 20 Q 40 60, 60 80 Q -10 160, 150 180" },
    // smooth quadratic curveto
    { "M10 20 T30 40""M 10 20 Q 10 20, 30 40" },
    { "M10 20 Q30 40 50 60 T70 80""M 10 20 Q 30 40, 50 60 Q 70 80, 70 80" },
    { "m10 20 q 30,40 50 60t-70,80",
      "M 10 20 Q 40 60, 60 80 Q 80 100, -10 160" },
    // elliptical arc. Exact numbers depend on too much math, so just verify
    // that these parse successfully
    { "M 1 3 A 1 2 3 00 6 7""path" },
    { "M 1 2 A 1 2 3 016 7""path" },
    { "M 1 2 A 1 2 3 10,6 7""path" },
    { "M 1 2 A 1 2 3 1,1 6 7""path" },
    { "M 1 2 A 1 2 3 1 1 6 7""path" },
    { "M 1 2 A 1 2 3 1 16 7""path" },
    // close path
    { "M10 20 Z""M 10 20 Z" },
    { "m10 20 30 40 m 50 60 70 80 90 100z""M 10 20 L 40 60 M 90 120 L 160 200 L 250 300 Z" },
    // must start with moveto
    { " L10 20", NULL },
    // moveto args
    { "M", NULL },
    { "M,", NULL },
    { "M10", NULL },
    { "M10,", NULL },
    { "M10x", NULL },
    { "M10,x", NULL },
    { "M10-20,", NULL },
    { "M10-20-30", NULL },
    { "M10-20-30 x", NULL },
    // closepath args
    { "M10-20z10", NULL },
    { "M10-20z,", NULL },
    // lineto args
    { "M10-20L10", NULL },
    { "M 10,10 L 20,20,30", NULL },
    { "M 10,10 L 20,20,", NULL },
    // horizontal lineto args
    { "M10-20H", NULL },
    { "M10-20H,", NULL },
    { "M10-20H30,", NULL },
    // vertical lineto args
    { "M10-20v", NULL },
    { "M10-20v,", NULL },
    { "M10-20v30,", NULL },
    // curveto args
    { "M10-20C1", NULL },
    { "M10-20C1,", NULL },
    { "M10-20C1 2", NULL },
    { "M10-20C1,2,", NULL },
    { "M10-20C1 2 3", NULL },
    { "M10-20C1,2,3", NULL },
    { "M10-20C1,2,3,", NULL },
    { "M10-20C1 2 3 4", NULL },
    { "M10-20C1,2,3,4", NULL },
    { "M10-20C1,2,3,4,", NULL },
    { "M10-20C1 2 3 4 5", NULL },
    { "M10-20C1,2,3,4,5", NULL },
    { "M10-20C1,2,3,4,5,", NULL },
    { "M10-20C1,2,3,4,5,6,", NULL },
    // smooth curveto args
    { "M10-20S1", NULL },
    { "M10-20S1,", NULL },
    { "M10-20S1 2", NULL },
    { "M10-20S1,2,", NULL },
    { "M10-20S1 2 3", NULL },
    { "M10-20S1,2,3,", NULL },
    { "M10-20S1,2,3,4,", NULL },
    // quadratic curveto args
    { "M10-20Q1", NULL },
    { "M10-20Q1,", NULL },
    { "M10-20Q1 2", NULL },
    { "M10-20Q1,2,", NULL },
    { "M10-20Q1 2 3", NULL },
    { "M10-20Q1,2,3", NULL },
    { "M10-20Q1,2,3,", NULL },
    { "M10 20 Q30 40 50 60,", NULL },
    // smooth quadratic curveto args
    { "M10-20T1", NULL },
    { "M10-20T1,", NULL },
    { "M10 20 T 30 40,", NULL },
    // elliptical arc args
    { "M10-20A1", NULL },
    { "M10-20A1,", NULL },
    { "M10-20A1 2", NULL },
    { "M10-20A1 2,", NULL },
    { "M10-20A1 2 3", NULL },
    { "M10-20A1 2 3,", NULL },
    { "M10-20A1 2 3 4", NULL },
    { "M10-20A1 2 3 1", NULL },
    { "M10-20A1 2 3,1,", NULL },
    { "M10-20A1 2 3 1 5", NULL },
    { "M10-20A1 2 3 1 1", NULL },
    { "M10-20A1 2 3,1,1,", NULL },
    { "M10-20A1 2 3 1 1 6", NULL },
    { "M10-20A1 2 3,1,1,6,", NULL },
    { "M 1 2 A 1 2 3 1.0 0.0 6 7", NULL },
    { "M10-20A1 2 3,1,1,6,7,", NULL },
    // misc
    { "M.. 1,0 0,100000", NULL },
    { "M 10 20,M 10 20", NULL },
    { "M 10 20, M 10 20", NULL },
    { "M 10 20, M 10 20", NULL },
    { "M 10 20, ", NULL },

    /* The following are not part of the rsvg tests */

    // an icon that was triggering broken handling of 'm'
    // Note that the output here is using 'M' to ensure the expected rendering
    { "M 4 1 c -1.660156 0 -3 1.339844 -3 3 v 8 c 0 1.660156 1.339844 3 3 3 h 8 c 1.660156 0 3 -1.339844 3 -3 v -8 c 0 -1.660156 -1.339844 -3 -3 -3 z m 0 6 h 8 v 2 h -8 z m 0 0""m 4 1 c -1.660156 0 -3 1.339844 -3 3 v 8 c 0 1.660156 1.339844 3 3 3 h 8 c 1.660156 0 3 -1.339844 3 -3 v -8 c 0 -1.660156 -1.339844 -3 -3 -3 z M 4 7 h 8 v 2 h -8 z m 0 0" },
    // another icon that was triggering broken handling of custom contours
    // Note that the output here is using 'M' to ensure the expected rendering
    { "m 2 0 c -1.089844 0 -2 0.910156 -2 2 v 3 c 0 1.089844 0.910156 2 2 2 h 3 c 1.089844 0 2 -0.910156 2 -2 v -3 c 0 -1.089844 -0.910156 -2 -2 -2 z "
      "m 8 0 c -1.089844 0 -2 0.910156 -2 2 v 3 c 0 1.089844 0.910156 2 2 2 h 3 c 1.089844 0 2 -0.910156 2 -2 v -3 c 0 -1.089844 -0.910156 -2 -2 -2 z "
      "m -8 2 h 3 v 3 h -3 z "
      "m 8 0 h 3 v 3 h -3 z "
      "m -8 6 c -1.089844 0 -2 0.910156 -2 2 v 3 c 0 1.089844 0.910156 2 2 2 h 3 c 1.089844 0 2 -0.910156 2 -2 v -3 c 0 -1.089844 -0.910156 -2 -2 -2 z "
      "m 8 0 c -1.089844 0 -2 0.910156 -2 2 v 3 c 0 1.089844 0.910156 2 2 2 h 3 c 1.089844 0 2 -0.910156 2 -2 v -3 c 0 -1.089844 -0.910156 -2 -2 -2 z "
      "m -8 2 h 3 v 3 h -3 z "
      "m 8 0 h 3 v 3 h -3 z",

      "M 2 0 c -1.089844 0 -2 0.910156 -2 2 v 3 c 0 1.089844 0.910156 2 2 2 h 3 c 1.089844 0 2 -0.910156 2 -2 v -3 c 0 -1.089844 -0.910156 -2 -2 -2 z "
      "M 10 0 c -1.089844 0 -2 0.910156 -2 2 v 3 c 0 1.089844 0.910156 2 2 2 h 3 c 1.089844 0 2 -0.910156 2 -2 v -3 c 0 -1.089844 -0.910156 -2 -2 -2 z "
      "M 2 2 h 3 v 3 h -3 z "
      "M 10 2 h 3 v 3 h -3 z "
      "M 2 8 c -1.089844 0 -2 0.910156 -2 2 v 3 c 0 1.089844 0.910156 2 2 2 h 3 c 1.089844 0 2 -0.910156 2 -2 v -3 c 0 -1.089844 -0.910156 -2 -2 -2 z "
      "M 10 8 c -1.089844 0 -2 0.910156 -2 2 v 3 c 0 1.089844 0.910156 2 2 2 h 3 c 1.089844 0 2 -0.910156 2 -2 v -3 c 0 -1.089844 -0.910156 -2 -2 -2 z "
      "M 2 10 h 3 v 3 h -3 z "
      "M 10 10 h 3 v 3 h -3 z" },
    // omitted commands
    { "M 10 10 20 30 z""M 10 10 L 20 30 z" },
    { "M 10 10 c 1 2 3 4 5 6 4 2 1 0 2 2 z",
      "M 10 10 c 1 2 3 4 5 6 c 4 2 1 0 2 2 z" },
    { "M 1 2 A 1 2 3 1 16 7 1 2 3 1 16 7""path" },
    // unclosed subpath
    { "M 10 10 20 30 20 10 10 10 M 40 40 50 50 50 40 Z m 20 0 h 4 v 4 h -4 z",
      "M 10 10 20 30 20 10 10 10 M 40 40 50 50 50 40 Z M 60 40 h 4 v 4 h -4 z" },
    // whitespace at end
    { "M 10 10 z ""M 10 10 z" },
    // unexpected char
    { "M 10 ;", NULL },
  };
  int i;

  for (i = 0; i < G_N_ELEMENTS (tests); i++)
    {
      GskPath *path;
      char *string;
      char *string2;

      if (g_test_verbose ())
        g_print ("%d: %s\n", i, tests[i].in);

      path = gsk_path_parse (tests[i].in);
      if (tests[i].out)
        {
          g_assert_nonnull (path);
          string = gsk_path_to_string (path);
          gsk_path_unref (path);

          if (strcmp (tests[i].out, "path") != 0)
            {
              /* Preferred, but doesn't work, because gsk_path_print()
               * prints numbers with insane accuracy
               */

              /* g_assert_cmpstr (tests[i].out, ==, string); */
              path = gsk_path_parse (tests[i].out);
              g_assert_nonnull (path);

              string2 = gsk_path_to_string (path);
              gsk_path_unref (path);

              g_assert_cmpstr (string, ==, string2);

              g_free (string2);
            }

          path = gsk_path_parse (string);
          g_assert_nonnull (path);

          string2 = gsk_path_to_string (path);
          gsk_path_unref (path);

          g_assert_cmpstr (string, ==, string2);

          g_free (string);
          g_free (string2);
        }
      else
        g_assert_null (path);
    }
}

static void
test_empty_path (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  char *s;
  graphene_rect_t bounds;
  GskPathPoint point;

  builder = gsk_path_builder_new ();
  path = gsk_path_builder_free_to_path (builder);

  g_assert_true (gsk_path_is_empty (path));
  g_assert_false (gsk_path_is_closed (path));

  s = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "");
  g_free (s);

  g_assert_false (gsk_path_get_bounds (path, &bounds));
  g_assert_true (graphene_rect_equal (&bounds, graphene_rect_zero ()));

  g_assert_false (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_WINDING));

  g_assert_false (gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (00), INFINITY, &point, NULL));

  g_assert_false (gsk_path_get_start_point (path, &point));
  g_assert_false (gsk_path_get_end_point (path, &point));

  gsk_path_unref (path);
}

static void
test_rect_path (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  char *s;
  graphene_rect_t bounds;
  GskPathPoint point;
  float distance;
  graphene_point_t p;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (00200100));
  path = gsk_path_builder_free_to_path (builder);

  g_assert_false (gsk_path_is_empty (path));
  g_assert_true (gsk_path_is_closed (path));

  s = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 0 0 h 200 v 100 h -200 z");
  g_free (s);

  g_assert_true (gsk_path_get_bounds (path, &bounds));
  g_assert_true (graphene_rect_equal (&bounds, &GRAPHENE_RECT_INIT (00200100)));

  g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (5050), GSK_FILL_RULE_WINDING));
  g_assert_false (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (200200), GSK_FILL_RULE_WINDING));

  g_assert_true (gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (200200), INFINITY, &point, &distance));

  g_assert_true (distance == 100);

  g_assert_true (gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (50, -10), INFINITY, &point, &distance));
  gsk_path_point_get_position (&point, path, &p);

  g_assert_true (distance == 10);
  g_assert_true (graphene_point_near (&p, &GRAPHENE_POINT_INIT (500), 0.1));

  gsk_path_unref (path);
}

/* test quad <> cubic conversions */
static gboolean
collect_path (GskPathOperation        op,
              const graphene_point_t *pts,
              gsize                   n_pts,
              float                   weight,
              gpointer                user_data)
{
  GskPathBuilder *builder = user_data;

  if (op == GSK_PATH_MOVE)
    return TRUE;

  switch (op)
    {
    case GSK_PATH_MOVE:
      break;

    case GSK_PATH_CLOSE:
      gsk_path_builder_close (builder);
      break;

    case GSK_PATH_LINE:
      gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
      break;

    case GSK_PATH_QUAD:
      gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y,
                                         pts[2].x, pts[2].y);
      break;

    case GSK_PATH_CUBIC:
      gsk_path_builder_cubic_to (builder, pts[1].x, pts[1].y,
                                          pts[2].x, pts[2].y,
                                          pts[3].x, pts[3].y);
      break;

    case GSK_PATH_CONIC:
      gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y,
                                          pts[2].x, pts[2].y,
                                          weight);
      break;

    default:
      g_assert_not_reached ();
    }

  return TRUE;
}

static void
test_foreach (void)
{
  const char *s = "M 0 0 Q 9 0, 9 9 Q 99 9, 99 18 Z";
  const char *sp = "M 0 0 C 6 0, 9 3, 9 9 C 69 9, 99 12, 99 18 Z";
  GskPath *path, *path2;
  char *s2;
  GskPathBuilder *builder;

  path = gsk_path_parse (s);

  builder = gsk_path_builder_new ();
  gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_QUAD, collect_path, builder);
  path2 = gsk_path_builder_free_to_path (builder);
  s2 = gsk_path_to_string (path2);

  g_assert_cmpstr (s, ==, s2);

  gsk_path_unref (path2);
  g_free (s2);

  builder = gsk_path_builder_new ();
  gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_QUAD|GSK_PATH_FOREACH_ALLOW_CUBIC, collect_path, builder);
  path2 = gsk_path_builder_free_to_path (builder);
  s2 = gsk_path_to_string (path2);

  g_assert_cmpstr (s, ==, s2);

  gsk_path_unref (path2);
  g_free (s2);

  builder = gsk_path_builder_new ();
  gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CUBIC, collect_path, builder);
  path2 = gsk_path_builder_free_to_path (builder);
  s2 = gsk_path_to_string (path2);

  /* We still end up with quads here, since GskPathBuilder aggressively reduces
   * curves degrees.
   */

  g_assert_cmpstr (s, ==, s2);

  gsk_path_unref (path2);
  g_free (s2);

  path2 = gsk_path_parse (sp);
  s2 = gsk_path_to_string (path2);

  g_assert_cmpstr (s, ==, s2);

  gsk_path_unref (path2);
  g_free (s2);

  gsk_path_unref (path);
}

static void
test_path_point (void)
{
  GskPath *path;
  GskPathPoint point;
  gboolean ret;
  graphene_point_t pos, center;
  graphene_vec2_t t1, t2, mx;
  float curvature;

  path = gsk_path_parse ("M 0 0 L 100 0 L 100 100 L 0 100 Z");

  ret = gsk_path_get_start_point (path, &point);
  g_assert_true (ret);

  g_assert_true (point.contour == 0);
  g_assert_true (point.idx == 1);
  g_assert_true (point.t == 0);

  ret = gsk_path_get_end_point (path, &point);
  g_assert_true (ret);

  g_assert_true (point.contour == 0);
  g_assert_true (point.idx == 4);
  g_assert_true (point.t == 1);

  ret = gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (200200), INFINITY, &point, NULL);
  g_assert_true (ret);

  g_assert_true (point.contour == 0);
  g_assert_true (point.idx == 2);
  g_assert_true (point.t == 1);

  gsk_path_point_get_position (&point, path, &pos);
  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_START, &t1);
  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_END, &t2);
  curvature = gsk_path_point_get_curvature (&point, path, GSK_PATH_FROM_START, ¢er);

  g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (100100)));
  g_assert_true (graphene_vec2_equal (&t1, graphene_vec2_y_axis ()));
  graphene_vec2_negate (graphene_vec2_x_axis (), &mx);
  g_assert_true (graphene_vec2_equal (&t2, &mx));
  g_assert_true (curvature == 0);

  ret = gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (10050), INFINITY, &point, NULL);
  g_assert_true (ret);

  g_assert_true (point.contour == 0);
  g_assert_true (point.idx == 2);
  g_assert_true (point.t == 0.5);

  gsk_path_unref (path);
}

/* Test that gsk_path_builder_add_segment yields the expected results */
static void
test_path_segments (void)
{
  struct {
    const char *path;
    graphene_point_t p1;
    graphene_point_t p2;
    const char *result;
  } tests[] = {
    {
      "M 0 0 L 100 0 L 50 50 Z",
      { 1000 },
      { 5050 },
      "M 100 0 L 50 50"
    },
    {
      "M 0 0 L 100 0 L 50 50 Z",
      { 500 },
      { 700 },
      "M 50 0 L 70 0"
    },
    {
      "M 0 0 L 100 0 L 50 50 Z",
      { 700 },
      { 500 },
      "M 70 0 L 100 0 L 50 50 L 0 0 L 50 0"
    },
    {
      "M 0 0 L 100 0 L 50 50 Z",
      { 500 },
      { 5050 },
      "M 50 0 L 100 0 L 50 50"
    },
    {
      "M 0 0 L 100 0 L 50 50 Z",
      { 1000 },
      { 1000 },
      "M 100 0 L 50 50 L 0 0 L 100 0"
    }
  };

  for (unsigned int i = 0; i < G_N_ELEMENTS (tests); i++)
    {
      GskPath *path;
      GskPathPoint p1, p2;
      GskPathBuilder *builder;
      GskPath *result;
      char *str;

      path = gsk_path_parse (tests[i].path);
      g_assert_true (gsk_path_get_closest_point (path, &tests[i].p1, INFINITY, &p1, NULL));
      g_assert_true (gsk_path_get_closest_point (path, &tests[i].p2, INFINITY, &p2, NULL));

      builder = gsk_path_builder_new ();
      gsk_path_builder_add_segment (builder, path, &p1, &p2);
      result = gsk_path_builder_free_to_path (builder);
      str = gsk_path_to_string (result);

      g_assert_cmpstr (str, ==, tests[i].result);

      g_free (str);
      gsk_path_unref (path);
    }
}

static void
test_bad_in_fill (void)
{
  GskPath *path;
  gboolean inside;

  /* A fat Cantarell W */
  path = gsk_path_parse ("M -2 694 M 206.1748046875 704 L 390.9371337890625 704 L 551.1888427734375 99.5035400390625 L 473.0489501953125 99.5035400390625 L 649.1048583984375 704 L 828.965087890625 704 L 1028.3077392578125 10 L 857.8111572265625 10 L 710.0489501953125 621.251708984375 L 775.9720458984375 598.426513671875 L 614.5245361328125 14.0489501953125 L 430.2237548828125 14.0489501953125 L 278.6783447265625 602.230712890625 L 330.0909423828125 602.230712890625 L 195.88818359375 10 L 5.7342529296875 10 L 206.1748046875 704 Z");

  /* The midpoint of the right foot of a fat Cantarell X */
  inside = gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (552.360107704.000000), GSK_FILL_RULE_WINDING);

  g_assert_false (inside);

  gsk_path_unref (path);
}

/* Test that path_in_fill implicitly closes contours. I think this is wrong,
 * but it is what "everybody" does.
 */

static void
test_unclosed_in_fill (void)
{
  GskPath *path;

  path = gsk_path_parse ("M 0 0 L 0 100 L 100 100 L 100 0 Z");
  g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (5050), GSK_FILL_RULE_WINDING));
  gsk_path_unref (path);

  path = gsk_path_parse ("M 0 0 L 0 100 L 100 100 L 100 0");
  g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (5050), GSK_FILL_RULE_WINDING));
  gsk_path_unref (path);
}

/* Test that all the gsk_path_builder_add methods close the current
 * contour in the end and do not change the current point.
 */

static void
test_path_builder_add (void)
{
  GskPathBuilder *builder;
  GskPath *path, *path2;
  char *s;
  cairo_path_t *cpath;
  cairo_surface_t *surface;
  cairo_t *cr;
  PangoLayout *layout;
  GskPathPoint point1, point2;

#define N_ADD_METHODS 8

  path = gsk_path_parse ("M 10 10 L 100 100");

  gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (5050), INFINITY, &point1, NULL);
  gsk_path_get_end_point (path, &point2);

  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 100100);
  cr = cairo_create (surface);
  cairo_move_to (cr, 1010);
  cairo_line_to (cr, 2030);
  cpath = cairo_copy_path (cr);

  layout = pango_cairo_create_layout (cr);
  pango_layout_set_text (layout, "ABC", -1);

  for (unsigned int i = 0; i < N_ADD_METHODS; i++)
    {
      builder = gsk_path_builder_new ();
      gsk_path_builder_move_to (builder, 123456);

      switch (i)
        {
        case 0:
          gsk_path_builder_add_path (builder, path);
          break;

        case 1:
          gsk_path_builder_add_reverse_path (builder, path);
          break;

        case 2:
          gsk_path_builder_add_segment (builder, path, &point1, &point2);
          break;

        case 3:
          gsk_path_builder_add_cairo_path (builder, cpath);
          break;

        case 4:
          gsk_path_builder_add_layout (builder, layout);
          break;

        case 5:
          gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (001010));
          break;

        case 6:
          {
            GskRoundedRect rect;

            gsk_rounded_rect_init (&rect,
                                   &GRAPHENE_RECT_INIT (00100100),
                                   &GRAPHENE_SIZE_INIT (1020),
                                   &GRAPHENE_SIZE_INIT (2030),
                                   &GRAPHENE_SIZE_INIT (00),
                                   &GRAPHENE_SIZE_INIT (1010));
            gsk_path_builder_add_rounded_rect (builder, &rect);
          }
          break;

        case 7:
          gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (00), 10);
          break;

        default:
          g_assert_not_reached ();
        }

      gsk_path_builder_rel_line_to (builder, 100);
      path2 = gsk_path_builder_free_to_path (builder);

      s = gsk_path_to_string (path2);
      g_assert_true (g_str_has_prefix (s, "M 123 456"));
      g_assert_true (g_str_has_suffix (s, "M 123 456 L 133 456"));
      g_free (s);
      gsk_path_unref (path2);
    }

  g_object_unref (layout);

  cairo_path_destroy (cpath);
  cairo_destroy (cr);
  cairo_surface_destroy (surface);

  gsk_path_unref (path);
}

static gboolean
rotate_path_cb (GskPathOperation        op,
                const graphene_point_t *pts,
                gsize                   n_pts,
                float                   weight,
                gpointer                user_data)
{
  GskPathBuilder **builders = user_data;

  switch (op)
    {
    case GSK_PATH_MOVE:
      gsk_path_builder_move_to (builders[0], pts[0].x, pts[0].y);
      gsk_path_builder_move_to (builders[1], pts[0].y, -pts[0].x);
      break;

    case GSK_PATH_CLOSE:
      gsk_path_builder_close (builders[0]);
      gsk_path_builder_close (builders[1]);
      break;

    case GSK_PATH_LINE:
      gsk_path_builder_line_to (builders[0], pts[1].x, pts[1].y);
      gsk_path_builder_line_to (builders[1], pts[1].y, -pts[1].x);
      break;

    case GSK_PATH_QUAD:
      gsk_path_builder_quad_to (builders[0], pts[1].x, pts[1].y, pts[2].x, pts[2].y);
      gsk_path_builder_quad_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x);
      break;

    case GSK_PATH_CUBIC:
      gsk_path_builder_cubic_to (builders[0], pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
      gsk_path_builder_cubic_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x, pts[3].y, -pts[3].x);
      break;

    case GSK_PATH_CONIC:
      gsk_path_builder_conic_to (builders[0], pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
      gsk_path_builder_conic_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x, weight);
      break;

    default:
      g_assert_not_reached ();
      return FALSE;
    }

  return TRUE;
}

static void
test_rotated_arc (void)
{
  GskPath *path;
  GskPathBuilder *builders[2];
  GskPath *paths[2];
  float x, y;
  GskFillRule fill_rule;

  path = gsk_path_parse ("M -963 186 O -375 -757, 537 -607 0.707");

  x = -626;
  y = -274;

  builders[0] = gsk_path_builder_new ();
  builders[1] = gsk_path_builder_new ();

  /* Use -1 here because we want all the flags, even future additions */
  gsk_path_foreach (path, -1, rotate_path_cb, builders);
  gsk_path_unref (path);

  paths[0] = gsk_path_builder_free_to_path (builders[0]);
  paths[1] = gsk_path_builder_free_to_path (builders[1]);

  fill_rule = GSK_FILL_RULE_EVEN_ODD;

  g_assert_true (gsk_path_in_fill (paths[0], &GRAPHENE_POINT_INIT (x, y), fill_rule)
                 ==
                 gsk_path_in_fill (paths[1], &GRAPHENE_POINT_INIT (y, -x), fill_rule));

  gsk_path_unref (paths[0]);
  gsk_path_unref (paths[1]);
}

static void
test_rounded_rect_basic (void)
{
  GskRoundedRect rect;
  GskPathBuilder *builder;
  GskPath *path;
  GskPathPoint point;
  graphene_point_t p;

  gsk_rounded_rect_init (&rect, &GRAPHENE_RECT_INIT (101010050),
                         &GRAPHENE_SIZE_INIT (00),
                         &GRAPHENE_SIZE_INIT (1010),
                         &GRAPHENE_SIZE_INIT (1030),
                         &GRAPHENE_SIZE_INIT (300));

  builder = gsk_path_builder_new ();

  gsk_path_builder_add_rounded_rect (builder, &rect);

  path = gsk_path_builder_free_to_path (builder);

  for (int i = 0; i < 100; i++)
    {
      p = GRAPHENE_POINT_INIT (g_test_rand_double_range (0200),
                               g_test_rand_double_range (0200));

      g_assert_true (gsk_rounded_rect_contains_point (&rect, &p) == gsk_path_in_fill (path, &p,&nbsp;GSK_FILL_RULE_WINDING));
    }

  gsk_path_get_start_point (path, &point);
  gsk_path_point_get_position (&point, path, &p);
  g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (1010)));

  gsk_path_get_end_point (path, &point);
  gsk_path_point_get_position (&point, path, &p);
  g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (1010)));

  gsk_path_unref (path);
}

static void
test_rect_in_fill (void)
{
  graphene_rect_t rect;
  GskPathBuilder *builder;
  GskPath *path;
  GskPathPoint point;
  graphene_point_t p;

  rect = GRAPHENE_RECT_INIT (101010050);

  builder = gsk_path_builder_new ();

  gsk_path_builder_add_rect (builder, &rect);

  path = gsk_path_builder_free_to_path (builder);

  for (int i = 0; i < 100; i++)
    {
      p = GRAPHENE_POINT_INIT (g_test_rand_double_range (0200),
                               g_test_rand_double_range (0200));

      g_assert_true (graphene_rect_contains_point (&rect, &p) == gsk_path_in_fill (path, &p,&nbsp;GSK_FILL_RULE_WINDING));
    }

  gsk_path_get_start_point (path, &point);
  gsk_path_point_get_position (&point, path, &p);
  g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (1010)));

  gsk_path_get_end_point (path, &point);
  gsk_path_point_get_position (&point, path, &p);
  g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (1010)));

  gsk_path_unref (path);
}

static void
test_circle_basic (void)
{
  GskPathBuilder *builder;
  GskPath *path, *path1, *path2, *path3, *path4, *path5, *path6;
  GskPathMeasure *measure, *measure1, *measure2, *measure3;
  float length, length1, length2, length3;
  GskPathPoint point0, point1;
  graphene_point_t p;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (00), 1);
  path = gsk_path_builder_free_to_path (builder);

  measure = gsk_path_measure_new (path);
  length = gsk_path_measure_get_length (measure);

  g_assert_cmpfloat_with_epsilon (length, 2 * M_PI, 0.001);

  gsk_path_get_start_point (path, &point0);
  gsk_path_point_get_position (&point0, path, &p);
  g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (10)));

  gsk_path_get_end_point (path, &point0);
  gsk_path_point_get_position (&point0, path, &p);
  g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (10)));

  gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (11), INFINITY, &point0, NULL);
  gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (-11), INFINITY, &point1, NULL);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_segment (builder, path, &point0, &point1);
  path1 = gsk_path_builder_free_to_path (builder);

  measure1 = gsk_path_measure_new (path1);
  length1 = gsk_path_measure_get_length (measure1);

  g_assert_cmpfloat_with_epsilon (length1, 2 * M_PI * 0.250.001);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_segment (builder, path, &point1, &point0);
  path2 = gsk_path_builder_free_to_path (builder);

  measure2 = gsk_path_measure_new (path2);
  length2 = gsk_path_measure_get_length (measure2);

  g_assert_cmpfloat_with_epsilon (length2, 2 * M_PI * 0.750.001);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_reverse_path (builder, path);
  path3 = gsk_path_builder_free_to_path (builder);

  measure3 = gsk_path_measure_new (path3);
  length3 = gsk_path_measure_get_length (measure3);

  g_assert_cmpfloat_with_epsilon (length3, 2 * M_PI, 0.001);

  g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_WINDING));
  g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_EVEN_ODD));
  g_assert_true (gsk_path_in_fill (path3, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_WINDING));
  g_assert_true (gsk_path_in_fill (path3, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_EVEN_ODD));

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_path (builder, path);
  gsk_path_builder_move_to (builder, -2, -2);
  gsk_path_builder_line_to (builder, 20);
  gsk_path_builder_line_to (builder, 22);
  gsk_path_builder_line_to (builder, -22);
  gsk_path_builder_close (builder);
  path4 = gsk_path_builder_free_to_path (builder);

  g_assert_true (gsk_path_in_fill (path4, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_WINDING));
  g_assert_false (gsk_path_in_fill (path4, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_EVEN_ODD));

  path5 = gsk_path_parse ("M 2 0 O 2 2 0 2 0.707 O -2 2 -2 0 0.707 O -2 -2 0 -2 0.707 O 2 -2 2 0 0.707 Z");
  builder = gsk_path_builder_new ();
  gsk_path_builder_add_path (builder, path);
  gsk_path_builder_add_path (builder, path5);
  path6 = gsk_path_builder_free_to_path (builder);

  g_assert_true (gsk_path_in_fill (path6, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_WINDING));
  g_assert_false (gsk_path_in_fill (path6, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_EVEN_ODD));

  gsk_path_measure_unref (measure);
  gsk_path_measure_unref (measure1);
  gsk_path_measure_unref (measure2);
  gsk_path_measure_unref (measure3);
  gsk_path_unref (path);
  gsk_path_unref (path1);
  gsk_path_unref (path2);
  gsk_path_unref (path3);
  gsk_path_unref (path4);
  gsk_path_unref (path5);
  gsk_path_unref (path6);
}

static void
test_length (void)
{
  GskPath *path, *path1, *path2;
  GskPathMeasure *measure, *measure1, *measure2;
  GskPathBuilder *builder;
  GskPathPoint point, start, end;
  float length, length1, length2;
  float distance;
  float tolerance = 0.1;

  path = gsk_path_parse ("M 0 0 Q 0 0 5 5");
  measure = gsk_path_measure_new_with_tolerance (path, tolerance);
  length = gsk_path_measure_get_length (measure);

  gsk_path_get_start_point (path, &start);
  gsk_path_get_end_point (path, &end);
  gsk_path_measure_get_point (measure, length / 2, &point);
  distance = gsk_path_point_get_distance (&point, measure);

  g_assert_cmpfloat_with_epsilon (length / 2, distance, 0.1);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_segment (builder, path, &start, &point);
  path1 = gsk_path_builder_free_to_path (builder);
  measure1 = gsk_path_measure_new_with_tolerance (path1, tolerance);
  length1 = gsk_path_measure_get_length (measure1);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_segment (builder, path, &point, &end);
  path2 = gsk_path_builder_free_to_path (builder);
  measure2 = gsk_path_measure_new_with_tolerance (path2, tolerance);
  length2 = gsk_path_measure_get_length (measure2);

  g_assert_cmpfloat_with_epsilon (length, length1 + length2, tolerance);

  gsk_path_unref (path);
  gsk_path_unref (path1);
  gsk_path_unref (path2);

  gsk_path_measure_unref (measure);
  gsk_path_measure_unref (measure1);
  gsk_path_measure_unref (measure2);
}

static void
test_rect_segment (void)
{
  GskPathBuilder *builder;
  GskPath *path, *path1, *path2;
  GskPathMeasure *measure, *measure1, *measure2;
  GskPathPoint point0, point1;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (00100100));
  path = gsk_path_builder_free_to_path (builder);
  measure = gsk_path_measure_new (path);

  gsk_path_measure_get_point (measure, 20, &point0);
  gsk_path_measure_get_point (measure, 80, &point1);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_segment (builder, path, &point0, &point1);
  path1 = gsk_path_builder_free_to_path (builder);
  measure1 = gsk_path_measure_new (path1);

  g_assert_cmpfloat_with_epsilon (gsk_path_measure_get_length (measure1), 600.001);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_segment (builder, path, &point1, &point0);
  path2 = gsk_path_builder_free_to_path (builder);
  measure2 = gsk_path_measure_new (path2);

  g_assert_cmpfloat_with_epsilon (gsk_path_measure_get_length (measure2), 3400.001);

  gsk_path_unref (path);
  gsk_path_unref (path1);
  gsk_path_unref (path2);
  gsk_path_measure_unref (measure);
  gsk_path_measure_unref (measure1);
  gsk_path_measure_unref (measure2);
}

static void
test_circle_point (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  GskPathPoint point;
  graphene_point_t center;
  float k;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (12), 0);
  path = gsk_path_builder_free_to_path (builder);

  gsk_path_get_start_point (path, &point);
  k = gsk_path_point_get_curvature (&point, path, GSK_PATH_TO_END, ¢er);

  g_assert_true (k == INFINITY);
  g_assert_true (graphene_point_equal (¢er, &GRAPHENE_POINT_INIT (12)));

  gsk_path_unref (path);
}

static void
test_rect_plain (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  GskPathMeasure *measure;
  GskPathPoint point;
  graphene_point_t pos;
  graphene_vec2_t tan, v;
  char *s;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (1010100200));
  path = gsk_path_builder_free_to_path (builder);

  s  = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 10 10 h 100 v 200 h -100 z");
  g_free (s);

  measure = gsk_path_measure_new (path);

  g_assert_true (gsk_path_measure_get_length (measure) == 600);

  gsk_path_get_start_point (path, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (1010)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 0, -1)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 01)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, -10)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 10)));

  gsk_path_measure_get_point (measure, 100, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (11010)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 10)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, -10)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 0, -1)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 01)));

  gsk_path_measure_get_point (measure, 300, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (110210)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 01)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 0, -1)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 10)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, -10)));

  gsk_path_measure_unref (measure);
  gsk_path_unref (path);
}

static void
test_rect_zero_height (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  GskPathMeasure *measure;
  GskPathPoint point;
  graphene_point_t pos;
  graphene_vec2_t tan, v;
  char *s;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (10101000));
  path = gsk_path_builder_free_to_path (builder);

  s  = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 10 10 h 100 v 0 h -100 z");
  g_free (s);

  measure = gsk_path_measure_new (path);

  g_assert_true (gsk_path_measure_get_length (measure) == 200);

  gsk_path_measure_get_point (measure, 100, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (11010)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 10)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, -10)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 10)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, -10)));

  gsk_path_measure_unref (measure);
  gsk_path_unref (path);
}

static void
test_rect_zero_width (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  GskPathMeasure *measure;
  GskPathPoint point;
  graphene_point_t pos;
  graphene_vec2_t tan, v;
  char *s;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (10100100));
  path = gsk_path_builder_free_to_path (builder);

  s  = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 10 10 h 0 v 100 h -0 z");
  g_free (s);

  measure = gsk_path_measure_new (path);

  g_assert_true (gsk_path_measure_get_length (measure) == 200);

  gsk_path_measure_get_point (measure, 100, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (10110)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 01)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 0, -1)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 01)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_END, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_init (&v, 0, -1)));

  gsk_path_measure_unref (measure);
  gsk_path_unref (path);
}

static void
test_rect_zero (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  GskPathMeasure *measure;
  GskPathPoint point;
  graphene_point_t pos;
  graphene_vec2_t tan;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (101000));
  path = gsk_path_builder_free_to_path (builder);
  measure = gsk_path_measure_new (path);

  g_assert_true (gsk_path_measure_get_length (measure) == 0);

  gsk_path_measure_get_point (measure, 0, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (1010)));

  gsk_path_point_get_tangent (&point, path, GSK_PATH_FROM_START, &tan);
  g_assert_true (graphene_vec2_equal (&tan, graphene_vec2_zero ()));

  gsk_path_measure_unref (measure);
  gsk_path_unref (path);
}

static void
check_path_point (const GskPathPoint     *point,
                  GskPath                *path,
                  const graphene_point_t *pos,
                  const graphene_vec2_t  *tan1,
                  const graphene_vec2_t  *tan2,
                  float                   k1,
                  float                   k2)
{
  graphene_point_t p;
  graphene_vec2_t tan;
  float ka1, ka2;

  gsk_path_point_get_position (point, path, &p);
  g_assert_true (graphene_point_near (pos, &p, 0.0001));

  gsk_path_point_get_tangent (point, path, GSK_PATH_FROM_START, &tan);
  g_assert_true (graphene_vec2_near (tan1, &tan, 0.0001));

  gsk_path_point_get_tangent (point, path, GSK_PATH_TO_END, &tan);
  g_assert_true (graphene_vec2_near (tan2, &tan, 0.0001));

  ka1 = gsk_path_point_get_curvature (point, path, GSK_PATH_FROM_START, NULL);
  if (isfinite (k1) && isfinite (ka1))
    g_assert_cmpfloat_with_epsilon (k1, ka1, 0.0001);
  else
    g_assert_true (isinf (k1) && isinf (ka1));

  ka2 = gsk_path_point_get_curvature (point, path, GSK_PATH_TO_END, NULL);
  if (isfinite (k2) && isfinite (ka2))
    g_assert_cmpfloat_with_epsilon (k2, ka2, 0.0001);
  else
    g_assert_true (isinf (k2) && isinf (ka2));
}

static void
test_zero_length (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  char *s;
  graphene_rect_t bounds;
  GskPathPoint point;
  graphene_vec2_t v1, v2;

  builder = gsk_path_builder_new ();
  gsk_path_builder_move_to (builder, 1010);
  path = gsk_path_builder_free_to_path (builder);

  g_assert_false (gsk_path_is_empty (path));
  g_assert_false (gsk_path_is_closed (path));

  s = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 10 10");
  g_free (s);

  g_assert_true (gsk_path_get_bounds (path, &bounds));
  g_assert_true (graphene_rect_equal (&bounds, &GRAPHENE_RECT_INIT (101000)));

  g_assert_false (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (00), GSK_FILL_RULE_WINDING));

  g_assert_true (gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (00), INFINITY, &point, NULL));

  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (1010),
                    graphene_vec2_init (&v1, 00),
                    graphene_vec2_init (&v2, 00),
                    00);

  g_assert_true (gsk_path_get_start_point (path, &point));
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (1010),
                    graphene_vec2_init (&v1, 00),
                    graphene_vec2_init (&v2, 00),
                    00);

  g_assert_true (gsk_path_get_end_point (path, &point));
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (1010),
                    graphene_vec2_init (&v1, 00),
                    graphene_vec2_init (&v2, 00),
                    00);

  gsk_path_unref (path);
}

static void
test_rounded_rect_plain (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  GskPathMeasure *measure;
  GskPathPoint point;
  GskRoundedRect rect;
  graphene_vec2_t v1, v2;
  graphene_point_t pos;
  char *s;

  rect.bounds = GRAPHENE_RECT_INIT (1010100200);
  rect.corner[0] = GRAPHENE_SIZE_INIT (1010);
  rect.corner[1] = GRAPHENE_SIZE_INIT (1010);
  rect.corner[2] = GRAPHENE_SIZE_INIT (1010);
  rect.corner[3] = GRAPHENE_SIZE_INIT (1010);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_rounded_rect (builder, &rect);
  path = gsk_path_builder_free_to_path (builder);

  s = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 20 10 L 100 10 O 110 10, 110 20, 0.707106769 L 110 200 O 110 210, 100 210, 0.707106769 L 20 210 O 10 210, 10 200, 0.707106769 L 10 20 O 10 10, 20 10, 0.707106769 Z");
  g_free (s);

  measure = gsk_path_measure_new (path);

  g_assert_cmpfloat_with_epsilon (gsk_path_measure_get_length (measure),
                                  600 - 4 * 20 + 2 * M_PI * 10,
                                  0.0001);

  gsk_path_get_start_point (path, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_near (&pos, &GRAPHENE_POINT_INIT (2010), 0.0001));

  /* Checking the curvature with the measured points is difficult,
   * because rounding might put us on the arc. Therefore, set points
   * precisely for these checks.
   */

  point.contour = 0;
  point.idx = 1;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (2010),
                    graphene_vec2_init (&v1, 10),
                    graphene_vec2_init (&v2, 10),
                    0.10);

  gsk_path_measure_get_point (measure, 80, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_near (&pos, &GRAPHENE_POINT_INIT (10010), 0.0001));

  point.contour = 0;
  point.idx = 2;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (10010),
                    graphene_vec2_init (&v1, 10),
                    graphene_vec2_init (&v2, 10),
                    00.1);

  gsk_path_measure_get_point (measure, 80 + (2 * M_PI * 10)/4, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_near (&pos, &GRAPHENE_POINT_INIT (11020), 0.0001));

  point.contour = 0;
  point.idx = 3;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (11020),
                    graphene_vec2_init (&v1, 01),
                    graphene_vec2_init (&v2, 01),
                    0.10);

  gsk_path_measure_get_point (measure, 80 + (2 * M_PI * 10)/4 + 180, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_near (&pos, &GRAPHENE_POINT_INIT (110200), 0.0001));

  point.contour = 0;
  point.idx = 4;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (110200),
                    graphene_vec2_init (&v1, 01),
                    graphene_vec2_init (&v2, 01),
                    00.1);

  gsk_path_measure_get_point (measure, 80 + 180 + (2 * M_PI * 10)/2, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_near (&pos, &GRAPHENE_POINT_INIT (100210), 0.00001));

  point.contour = 0;
  point.idx = 5;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (100210),
                    graphene_vec2_init (&v1, -10),
                    graphene_vec2_init (&v2, -10),
                    0.10);

  gsk_path_measure_get_point (measure, 80 + 180 + 80 + (2 * M_PI * 10)/2, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_near (&pos, &GRAPHENE_POINT_INIT (20210), 0.00001));

  point.contour = 0;
  point.idx = 6;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (20210),
                    graphene_vec2_init (&v1, -10),
                    graphene_vec2_init (&v2, -10),
                    00.1);

  gsk_path_measure_get_point (measure, 80 + 180 + 80 + (2 * M_PI * 10)*3/4, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_near (&pos, &GRAPHENE_POINT_INIT (10200), 0.00001));

  point.contour = 0;
  point.idx = 7;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (10200),
                    graphene_vec2_init (&v1, 0, -1),
                    graphene_vec2_init (&v2, 0, -1),
                    0.10);

  gsk_path_measure_get_point (measure, 80 + 180 + 80 + 180 + (2 * M_PI * 10)*3/4, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_near (&pos, &GRAPHENE_POINT_INIT (1020), 0.00001));

  point.contour = 0;
  point.idx = 8;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (1020),
                    graphene_vec2_init (&v1, 0, -1),
                    graphene_vec2_init (&v2, 0, -1),
                    00.1);

  gsk_path_measure_unref (measure);
  gsk_path_unref (path);
}

static void
test_rounded_rect_tricky (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  GskPathMeasure *measure;
  GskPathPoint point;
  GskRoundedRect rect;
  graphene_vec2_t v1, v2;
  graphene_point_t pos;
  char *s;

  rect.bounds = GRAPHENE_RECT_INIT (1010100200);
  rect.corner[0] = GRAPHENE_SIZE_INIT (00);
  rect.corner[1] = GRAPHENE_SIZE_INIT (0100);
  rect.corner[2] = GRAPHENE_SIZE_INIT (0100);
  rect.corner[3] = GRAPHENE_SIZE_INIT (1010);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_rounded_rect (builder, &rect);
  path = gsk_path_builder_free_to_path (builder);

  s = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 10 10 L 110 10 L 110 110 L 110 210 L 20 210 O 10 210, 10 200, 0.707106769 L 10 10 Z");
  g_free (s);

  measure = gsk_path_measure_new (path);

  g_assert_cmpfloat_with_epsilon (gsk_path_measure_get_length (measure),
                                  600 - 20 + (2 * M_PI * 10) / 4,
                                  0.0001);

  gsk_path_get_start_point (path, &point);
  gsk_path_point_get_position (&point, path, &pos);
  g_assert_true (graphene_point_near (&pos, &GRAPHENE_POINT_INIT (1010), 0.0001));

  /* Checking the curvature with the measured points is difficult,
   * because rounding might put us on the arc. Therefore, set points
   * precisely for these checks.
   */

  point.contour = 0;
  point.idx = 1;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (1010),
                    graphene_vec2_init (&v1, 0, -1),
                    graphene_vec2_init (&v2, 10),
                    00);

  point.contour = 0;
  point.idx = 2;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (11010),
                    graphene_vec2_init (&v2, 10),
                    graphene_vec2_init (&v1, 01),
                    00);

  point.contour = 0;
  point.idx = 3;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (110110),
                    graphene_vec2_init (&v1, 01),
                    graphene_vec2_init (&v2, 01),
                    00);

  point.contour = 0;
  point.idx = 4;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (110210),
                    graphene_vec2_init (&v1, 01),
                    graphene_vec2_init (&v2, -10),
                    00);

  point.contour = 0;
  point.idx = 5;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (20210),
                    graphene_vec2_init (&v1, -10),
                    graphene_vec2_init (&v2, -10),
                    00.1);

  point.contour = 0;
  point.idx = 6;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (10200),
                    graphene_vec2_init (&v2, 0, -1),
                    graphene_vec2_init (&v2, 0, -1),
                    0.10);

  point.contour = 0;
  point.idx = 7;
  point.t = 0;
  check_path_point (&point, path,
                    &GRAPHENE_POINT_INIT (1010),
                    graphene_vec2_init (&v1, 0, -1),
                    graphene_vec2_init (&v2, 10),
                    00);

  gsk_path_measure_unref (measure);
  gsk_path_unref (path);
}

static void
test_rounded_rect_parse (void)
{
  GskRoundedRect rr;
  GskPathBuilder *builder;
  GskPath *path, *path2;
  char *s;

  rr.bounds = GRAPHENE_RECT_INIT (1010100111);
  rr.corner[GSK_CORNER_TOP_LEFT] = GRAPHENE_SIZE_INIT (24);
  rr.corner[GSK_CORNER_TOP_RIGHT] = GRAPHENE_SIZE_INIT (68);
  rr.corner[GSK_CORNER_BOTTOM_RIGHT] = GRAPHENE_SIZE_INIT (1012);
  rr.corner[GSK_CORNER_BOTTOM_LEFT] = GRAPHENE_SIZE_INIT (1416);

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_rounded_rect (builder, &rr);
  path = gsk_path_builder_free_to_path (builder);

  s = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 12 10 L 104 10 O 110 10, 110 18, 0.707106769 L 110 109 O 110 121, 100 121, 0.707106769 L 24 121 O 10 121, 10 105, 0.707106769 L 10 14 O 10 10, 12 10, 0.707106769 Z");

  path2 = gsk_path_parse (s);
  g_free (s);

  s = gsk_path_to_string (path2);
  g_assert_cmpstr (s, ==, "M 12 10 L 104 10 O 110 10, 110 18, 0.707106769 L 110 109 O 110 121, 100 121, 0.707106769 L 24 121 O 10 121, 10 105, 0.707106769 L 10 14 O 10 10, 12 10, 0.707106769 Z");
  g_free (s);

  gsk_path_unref (path);
  gsk_path_unref (path2);
}

static void
test_circle_plain (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  GskPathMeasure *measure;
  GskPathPoint point;
  graphene_vec2_t v1;
  graphene_point_t pos, center;
  char *s;
  float angle, radius;

  center = GRAPHENE_POINT_INIT (100100);
  radius = 10;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_circle (builder, ¢er, radius);
  path = gsk_path_builder_free_to_path (builder);

  s = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 110 100 o 0 10, -10 10, 0.707106769 o -10 0, -10 -10, 0.707106769 o 0 -10, 10 -10, 0.707106769 o 10 0, 10 10, 0.707106769 z");
  g_free (s);

  measure = gsk_path_measure_new (path);

  g_assert_cmpfloat_with_epsilon (gsk_path_measure_get_length (measure),
                                  2 * M_PI * radius,
                                  0.0001);

  gsk_path_get_start_point (path, &point);
  g_assert_true (point.idx == 1 && point.t == 0);

  gsk_path_get_end_point (path, &point);
  g_assert_true (point.idx == 5 && point.t == 1);

  for (int i = 0; i < 8; i++)
    {
      angle = 2 * M_PI * (i + 1) / 8.f;
      gsk_path_measure_get_point (measure, angle * radius, &point);

      pos = GRAPHENE_POINT_INIT (100 + cosf (angle) * radius, 100 + sinf (angle) * radius);
      graphene_vec2_init (&v1, - sinf (angle), cosf (angle));

      check_path_point (&point, path,
                        &pos,
                        &v1, &v1,
                        0.10.1);
    }

  gsk_path_measure_unref (measure);
  gsk_path_unref (path);
}

static void
test_circle_zero (void)
{
  GskPathBuilder *builder;
  GskPath *path;
  GskPathMeasure *measure;
  GskPathPoint point;
  graphene_vec2_t v1;
  graphene_point_t pos, center;
  char *s;
  float angle, radius;

  center = GRAPHENE_POINT_INIT (100100);
  radius = 0;

  builder = gsk_path_builder_new ();
  gsk_path_builder_add_circle (builder, ¢er, radius);
  path = gsk_path_builder_free_to_path (builder);

  s = gsk_path_to_string (path);
  g_assert_cmpstr (s, ==, "M 100 100 o 0 0, 0 0, 0.707106769 o 0 0, 0 0, 0.707106769 o 0 0, 0 0, 0.707106769 o 0 0, 0 0, 0.707106769 z");
  g_free (s);

  measure = gsk_path_measure_new (path);

  g_assert_cmpfloat_with_epsilon (gsk_path_measure_get_length (measure),
                                  2 * M_PI * radius,
                                  0.0001);

  for (int i = 0; i < 8; i++)
    {
      angle = 2 * M_PI * (i + 1) / 8.f;
      gsk_path_measure_get_point (measure, angle * radius, &point);

      pos = center;
      graphene_vec2_init (&v1, 00);

      check_path_point (&point, path,
                        &pos,
                        &v1, &v1,
                        INFINITY, INFINITY);
    }

  gsk_path_measure_unref (measure);
  gsk_path_unref (path);
}

static void
test_large_coord_conics (void)
{
  GskPath *path = gsk_path_parse ("M 2 10 o 0 2, -2 2, 0.707106769 o -2 0, -2 -2, 0.707106769 o 0 -2, 2 -2, 0.707106769 o 2 0, 2 2, 0.707106769 z M 3000002 10 o 0 2, -2 2, 0.707106769 o -2 0, -2 -2, 0.707106769 o 0 -2, 2 -2, 0.70710676");
  cairo_t *cr;
  cairo_surface_t *surface;

  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 100100);
  cr = cairo_create (surface);

  /* Check that this does not cause a stack overflow due to
   * runaway recursion.
   */

  gsk_path_to_cairo (path, cr);

  cairo_surface_destroy (surface);
  cairo_destroy (cr);

  gsk_path_unref (path);
}

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

  g_test_add_func ("/path/rsvg-parse", test_rsvg_parse);
  g_test_add_func ("/path/empty", test_empty_path);
  g_test_add_func ("/path/rect/path", test_rect_path);
  g_test_add_func ("/path/foreach", test_foreach);
  g_test_add_func ("/path/point", test_path_point);
  g_test_add_func ("/path/segments", test_path_segments);
  g_test_add_func ("/path/bad-in-fill", test_bad_in_fill);
  g_test_add_func ("/path/unclosed-in-fill", test_unclosed_in_fill);
  g_test_add_func ("/path/builder/add", test_path_builder_add);
  g_test_add_func ("/path/rotated-arc", test_rotated_arc);
  g_test_add_func ("/path/rounded-rect/basic", test_rounded_rect_basic);
  g_test_add_func ("/path/rect/in-fill", test_rect_in_fill);
  g_test_add_func ("/path/circle/basic", test_circle_basic);
  g_test_add_func ("/path/length", test_length);
  g_test_add_func ("/path/rect/segment", test_rect_segment);
  g_test_add_func ("/path/circle/point", test_circle_point);
  g_test_add_func ("/path/rect/plain", test_rect_plain);
  g_test_add_func ("/path/rect/zero-height", test_rect_zero_height);
  g_test_add_func ("/path/rect/zero-width", test_rect_zero_width);
  g_test_add_func ("/path/rect/zero", test_rect_zero);
  g_test_add_func ("/path/rounded-rect/plain", test_rounded_rect_plain);
  g_test_add_func ("/path/rounded-rect/tricky", test_rounded_rect_tricky);
  g_test_add_func ("/path/rounded-rect/parse", test_rounded_rect_parse);
  g_test_add_func ("/path/circle/plain", test_circle_plain);
  g_test_add_func ("/path/circle/zero", test_circle_zero);
  g_test_add_func ("/path/zero-length", test_zero_length);
  g_test_add_func ("/path/large-coord-conics", test_large_coord_conics);

  return g_test_run ();
}

Messung V0.5 in Prozent
C=99 H=90 G=94

¤ Dauer der Verarbeitung: 0.51 Sekunden  (vorverarbeitet am  2026-07-02) ¤

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik