path: Fix gsk_path_measure_in_fill
This was getting some simple polygon cases wrong :( The problem here is a misunderstanding in line_get_crossing. It was returning 'on edge' when a point of the polygon is on the ray, which is not what 'on edge' is about. Add some test cases involving horizontal edges that coincide with the test point.
This commit is contained in:
@@ -1583,6 +1583,9 @@ gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns twice the winding number, so we can record the
|
||||
* 'point on ray' case by returning an odd number.
|
||||
*/
|
||||
static inline int
|
||||
line_get_crossing (const graphene_point_t *p,
|
||||
const graphene_point_t *p1,
|
||||
@@ -1590,6 +1593,7 @@ line_get_crossing (const graphene_point_t *p,
|
||||
gboolean *on_edge)
|
||||
{
|
||||
int dir = 1;
|
||||
float r;
|
||||
|
||||
if (p1->x >= p->x && p2->x >= p->x)
|
||||
return 0;
|
||||
@@ -1603,21 +1607,32 @@ line_get_crossing (const graphene_point_t *p,
|
||||
dir = -1;
|
||||
}
|
||||
|
||||
if ((p1->x >= p->x && p1->y == p->y) ||
|
||||
(p2->x >= p->x && p2->y == p->y))
|
||||
if ((p1->x <= p->x && p1->y == p->y) ||
|
||||
(p2->x <= p->x && p2->y == p->y))
|
||||
{
|
||||
*on_edge = TRUE;
|
||||
return 0;
|
||||
if (p1->y == p2->y)
|
||||
{
|
||||
if (p1->x >= p->x || p2->x >= p->x)
|
||||
*on_edge = TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
if (p2->y <= p->y || p1->y > p->y)
|
||||
return 0;
|
||||
|
||||
if (p1->x <= p->x && p2->x <= p->x)
|
||||
return dir;
|
||||
return 2 * dir;
|
||||
|
||||
if (p->x > p1->x + (p->y - p1->y) * (p2->x - p1->x) / (p2->y - p1->y))
|
||||
return dir;
|
||||
r = p1->x + (p->y - p1->y) * (p2->x - p1->x) / (p2->y - p1->y);
|
||||
if (p->x > r)
|
||||
return 2 * dir;
|
||||
|
||||
if (p->x == r)
|
||||
*on_edge = TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1653,6 +1668,8 @@ gsk_standard_contour_get_winding (const GskContour *contour,
|
||||
|
||||
winding += line_get_crossing (point, &last_point, &self->points[0], on_edge);
|
||||
|
||||
winding /= 2;
|
||||
|
||||
return winding;
|
||||
}
|
||||
|
||||
|
||||
@@ -306,6 +306,39 @@ test_serialize_custom_contours (void)
|
||||
gsk_path_unref (path1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_path_winding (void)
|
||||
{
|
||||
struct {
|
||||
const char *path;
|
||||
graphene_point_t point;
|
||||
gboolean result;
|
||||
} tests[] = {
|
||||
{ "M 150 103 L 250 100 L 300 103 L 250 180 Z", GRAPHENE_POINT_INIT (175, 100), FALSE },
|
||||
{ "M 100 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (250, 100), TRUE },
|
||||
{ "M 100 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (400, 100), FALSE},
|
||||
{ "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (50, 100), FALSE },
|
||||
{ "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (150, 100), TRUE },
|
||||
{ "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (250, 100), TRUE },
|
||||
{ "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (400, 100), FALSE },
|
||||
};
|
||||
GskPath *path;
|
||||
GskPathMeasure *measure;
|
||||
gboolean res;
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
path = gsk_path_parse (tests[i].path);
|
||||
measure = gsk_path_measure_new (path);
|
||||
|
||||
res = gsk_path_measure_in_fill (measure, &tests[i].point, GSK_FILL_RULE_WINDING);
|
||||
g_assert_true (res == tests[i].result);
|
||||
|
||||
gsk_path_measure_unref (measure);
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
@@ -314,6 +347,7 @@ main (int argc,
|
||||
|
||||
g_test_add_func ("/path/rsvg-parse", test_rsvg_parse);
|
||||
g_test_add_func ("/path/serialize-custom-contours", test_serialize_custom_contours);
|
||||
g_test_add_func ("/path/winding", test_path_winding);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user