/*
* 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>
#include "path-utils.h"
typedef struct {
GskPathOperation op;
graphene_point_t pts[4 ];
} PathOperation;
static void
_g_string_append_double (GString *string,
double d)
{
char buf[G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, d);
g_string_append (string, buf);
}
static void
_g_string_append_point (GString *string,
const graphene_point_t *pt)
{
_g_string_append_double (string, pt->x);
g_string_append_c (string, ' ' );
_g_string_append_double (string, pt->y);
}
static void
path_operation_print (const PathOperation *p,
GString *string)
{
switch (p->op)
{
case GSK_PATH_MOVE:
g_string_append (string, "M " );
_g_string_append_point (string, &p->pts[0 ]);
break ;
case GSK_PATH_CLOSE:
g_string_append (string, " Z" );
break ;
case GSK_PATH_LINE:
g_string_append (string, " L " );
_g_string_append_point (string, &p->pts[1 ]);
break ;
case GSK_PATH_QUAD:
g_string_append (string, " Q " );
_g_string_append_point (string, &p->pts[1 ]);
g_string_append (string, ", " );
_g_string_append_point (string, &p->pts[2 ]);
break ;
case GSK_PATH_CUBIC:
g_string_append (string, " C " );
_g_string_append_point (string, &p->pts[1 ]);
g_string_append (string, ", " );
_g_string_append_point (string, &p->pts[2 ]);
g_string_append (string, ", " );
_g_string_append_point (string, &p->pts[3 ]);
break ;
case GSK_PATH_CONIC:
g_string_append (string, " O " );
_g_string_append_point (string, &p->pts[1 ]);
g_string_append (string, ", " );
_g_string_append_point (string, &p->pts[3 ]);
g_string_append (string, ", " );
_g_string_append_double (string, p->pts[2 ].x);
break ;
default :
g_assert_not_reached();
return ;
}
}
static gboolean
path_operation_equal (const PathOperation *p1,
const PathOperation *p2,
float epsilon)
{
if (p1->op != p2->op)
return FALSE ;
/* No need to compare pts[0] for most ops, that's just
* duplicate work. */
switch (p1->op)
{
case GSK_PATH_MOVE:
return graphene_point_near (&p1->pts[0 ], &p2->pts[0 ], epsilon);
case GSK_PATH_LINE:
case GSK_PATH_CLOSE:
return graphene_point_near (&p1->pts[1 ], &p2->pts[1 ], epsilon);
case GSK_PATH_QUAD:
return graphene_point_near (&p1->pts[1 ], &p2->pts[1 ], epsilon)
&& graphene_point_near (&p1->pts[2 ], &p2->pts[2 ], epsilon);
case GSK_PATH_CUBIC:
return graphene_point_near (&p1->pts[1 ], &p2->pts[1 ], epsilon)
&& graphene_point_near (&p1->pts[2 ], &p2->pts[2 ], epsilon)
&& graphene_point_near (&p1->pts[3 ], &p2->pts[3 ], epsilon);
case GSK_PATH_CONIC:
return graphene_point_near (&p1->pts[1 ], &p2->pts[1 ], epsilon)
&& graphene_point_near (&p1->pts[3 ], &p2->pts[3 ], epsilon);
default :
g_return_val_if_reached (FALSE );
}
}
static gboolean
collect_path_operation_cb (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
g_array_append_vals (user_data,
(PathOperation[1 ]) { {
op,
{
GRAPHENE_POINT_INIT(pts[0 ].x, pts[0 ].y),
GRAPHENE_POINT_INIT(n_pts > 1 ? pts[1 ].x : 0 ,
n_pts > 1 ? pts[1 ].y : 0 ),
GRAPHENE_POINT_INIT(n_pts > 2 ? pts[2 ].x : 0 ,
n_pts > 2 ? pts[2 ].y : 0 ),
GRAPHENE_POINT_INIT(n_pts > 3 ? pts[3 ].x : 0 ,
n_pts > 3 ? pts[3 ].y : 0 )
},
} },
1 );
return TRUE ;
}
static GArray *
collect_path (GskPath *path)
{
GArray *array = g_array_new (FALSE , FALSE , sizeof (PathOperation));
/* Use -1 here because we want all the flags, even future additions */
gsk_path_foreach (path, -1 , collect_path_operation_cb, array);
return array;
}
void
assert_path_equal_func (const char *domain,
const char *file,
int line,
const char *func,
GskPath *path1,
GskPath *path2,
float epsilon)
{
GArray *ops1, *ops2;
guint i;
ops1 = collect_path (path1);
ops2 = collect_path (path2);
for (i = 0 ; i < MAX (ops1->len, ops2->len); i++)
{
PathOperation *op1 = i < ops1->len ? &g_array_index (ops1, PathOperation, i) : NULL;
PathOperation *op2 = i < ops2->len ? &g_array_index (ops2, PathOperation, i) : NULL;
if (op1 == NULL || op2 == NULL || !path_operation_equal (op1, op2, epsilon))
{
GString *string;
guint j;
/* Find the operation we start to print */
for (j = i; j-- > 0 ; )
{
PathOperation *op = &g_array_index (ops1, PathOperation, j);
if (op->op == GSK_PATH_MOVE)
break ;
if (j + 3 == i)
{
j = i - 1 ;
break ;
}
}
string = g_string_new (j == 0 ? "" : "... " );
for (; j < i; j++)
{
PathOperation *op = &g_array_index (ops1, PathOperation, j);
path_operation_print (op, string);
g_string_append_c (string, ' ' );
}
g_string_append (string, "\\\n " );
if (op1)
{
path_operation_print (op1, string);
if (ops1->len > i + 1 )
g_string_append (string, " ..." );
}
g_string_append (string, "\n " );
if (op1)
{
path_operation_print (op2, string);
if (ops2->len > i + 1 )
g_string_append (string, " ..." );
}
g_assertion_message (domain, file, line, func, string->str);
g_string_free (string, TRUE );
}
}
g_array_free (ops1, TRUE );
g_array_free (ops2, TRUE );
}
Messung V0.5 in Prozent C=98 H=93 G=95
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.9Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-07-02)
¤
*Eine klare Vorstellung vom Zielzustand