Merge branch 'contour-cosmetics' into 'main'

contour: Fixes for circles

See merge request GNOME/gtk!6399
This commit is contained in:
Matthias Clasen 2023-09-16 13:18:29 +00:00
commit 36c4dc8aea
2 changed files with 299 additions and 119 deletions

View File

@ -1346,19 +1346,32 @@ gsk_circle_contour_print (const GskContour *contour,
GString *string)
{
const GskCircleContour *self = (const GskCircleContour *) contour;
float radius, radius_neg;
_g_string_append_point (string, "M ", &GRAPHENE_POINT_INIT (self->center.x + self->radius, self->center.y));
_g_string_append_point (string, " o ", &GRAPHENE_POINT_INIT (0, self->radius));
_g_string_append_point (string, ", ", &GRAPHENE_POINT_INIT (- self->radius, self->radius));
if (self->radius > 0)
{
radius = self->radius;
radius_neg = - self->radius;
}
else
{
radius = 0.f;
radius_neg = 0.f;
}
_g_string_append_point (string, "M ", &GRAPHENE_POINT_INIT (self->center.x + radius, self->center.y));
_g_string_append_point (string, " o ", &GRAPHENE_POINT_INIT (0, radius));
_g_string_append_point (string, ", ", &GRAPHENE_POINT_INIT (radius_neg, radius));
_g_string_append_double (string, ", ", M_SQRT1_2);
_g_string_append_point (string, " o ", &GRAPHENE_POINT_INIT (- self->radius, 0));
_g_string_append_point (string, ", ", &GRAPHENE_POINT_INIT (- self->radius, - self->radius));
_g_string_append_point (string, " o ", &GRAPHENE_POINT_INIT (radius_neg, 0));
_g_string_append_point (string, ", ", &GRAPHENE_POINT_INIT (radius_neg, radius_neg));
_g_string_append_double (string, ", ", M_SQRT1_2);
_g_string_append_point (string, " o ", &GRAPHENE_POINT_INIT (0, - self->radius));
_g_string_append_point (string, ", ", &GRAPHENE_POINT_INIT (self->radius, - self->radius));
_g_string_append_point (string, " o ", &GRAPHENE_POINT_INIT (0, radius_neg));
_g_string_append_point (string, ", ", &GRAPHENE_POINT_INIT (radius, radius_neg));
_g_string_append_double (string, ", ", M_SQRT1_2);
_g_string_append_point (string, " o ", &GRAPHENE_POINT_INIT (self->radius, 0));
_g_string_append_point (string, ", ", &GRAPHENE_POINT_INIT (self->radius, self->radius));
_g_string_append_point (string, " o ", &GRAPHENE_POINT_INIT (radius, 0));
_g_string_append_point (string, ", ", &GRAPHENE_POINT_INIT (radius, radius));
_g_string_append_double (string, ", ", M_SQRT1_2);
g_string_append (string, " z");
}
@ -1419,10 +1432,10 @@ gsk_circle_contour_foreach (const GskContour *contour,
pts[9] = GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y);
return func (GSK_PATH_MOVE, &pts[0], 1, 0.f, user_data) &&
func (GSK_PATH_CONIC, &pts[0], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_CONIC, &pts[2], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_CONIC, &pts[4], 3, M_SQRT1_2, user_data) &&
func (GSK_PATH_CONIC, &pts[6], 3, M_SQRT1_2, user_data) &&
maybe_emit_conic (&pts[0], M_SQRT1_2, func, user_data) &&
maybe_emit_conic (&pts[2], M_SQRT1_2, func, user_data) &&
maybe_emit_conic (&pts[4], M_SQRT1_2, func, user_data) &&
maybe_emit_conic (&pts[6], M_SQRT1_2, func, user_data) &&
func (GSK_PATH_CLOSE, &pts[8], 2, 0.f, user_data);
}
@ -1454,10 +1467,12 @@ gsk_circle_contour_get_winding (const GskContour *contour,
static gsize
gsk_circle_contour_get_n_ops (const GskContour *contour)
{
const GskCircleContour *self = (const GskCircleContour *) contour;
/* idx == 0 is the move (which does not really exist here,
* but gskpath.c assumes there is one).
*/
return 2;
return self->radius > 0 ? 6 : 2;
}
static gboolean
@ -1469,6 +1484,7 @@ gsk_circle_contour_get_closest_point (const GskContour *contour,
{
const GskCircleContour *self = (const GskCircleContour *) contour;
float dist, angle, t;
gsize idx;
dist = fabsf (graphene_point_distance (&self->center, point, NULL, NULL) - self->radius);
@ -1485,7 +1501,16 @@ gsk_circle_contour_get_closest_point (const GskContour *contour,
if (self->ccw)
t = 1 - t;
result->idx = 1;
t = t * 4;
idx = 1;
do {
if (t < 1)
break;
t = t - 1;
idx = idx + 1;
} while (t != 0);
result->idx = idx;
result->t = t;
return TRUE;
@ -1497,18 +1522,38 @@ gsk_circle_contour_get_position (const GskContour *contour,
graphene_point_t *position)
{
const GskCircleContour *self = (const GskCircleContour *) contour;
float t;
gsize idx = point->idx;
float t = point->t;
t = point->t;
if (self->radius == 0)
{
*position = self->center;
return;
}
/* avoid the z */
if (idx == 5)
{
idx = 4;
t = 1;
}
if (self->ccw)
t = 1 - t;
{
idx = 5 - idx;
t = 1 - t;
}
if (t == 0 || t == 1)
*position = GRAPHENE_POINT_INIT (self->center.x + self->radius, self->center.y);
if ((idx == 1 && t == 0) || (idx == 4 && t == 1))
{
*position = GRAPHENE_POINT_INIT (self->center.x + self->radius, self->center.y);
}
else
*position = GRAPHENE_POINT_INIT (self->center.x + cosf (t * 2 * M_PI) * self->radius,
self->center.y + sinf (t * 2 * M_PI) * self->radius);
{
float angle = M_PI_2 * ((idx - 1) + t);
*position = GRAPHENE_POINT_INIT (self->center.x + cosf (angle) * self->radius,
self->center.y + sinf (angle) * self->radius);
}
}
static void
@ -1522,7 +1567,7 @@ gsk_circle_contour_get_tangent (const GskContour *contour,
gsk_circle_contour_get_position (contour, point, &p);
graphene_vec2_init (tangent, p.y - self->center.y, - p.x + self->center.x);
graphene_vec2_init (tangent, - p.y + self->center.y, p.x - self->center.x);
graphene_vec2_normalize (tangent, tangent);
}
@ -1621,18 +1666,33 @@ gsk_circle_contour_get_point (const GskContour *contour,
{
const GskCircleContour *self = (const GskCircleContour *) contour;
float t;
gsize idx;
if (self->radius == 0)
t = 0;
else
t = distance / (2 * M_PI * self->radius);
{
result->idx = 1;
result->t = 0;
return;
}
t = distance / (M_PI_2 * self->radius);
idx = 1;
do {
if (t < 1)
break;
t = t - 1;
idx = idx + 1;
} while (t > 0);
if (self->ccw)
t = 1 - t;
{
idx = 5 - idx;
t = 1 - t;
}
result->idx = 1;
result->idx = idx;
result->t = t;
g_assert (result->t >= 0 && result->t <= 1);
}
static float
@ -1642,13 +1702,21 @@ gsk_circle_contour_get_distance (const GskContour *contour,
{
const GskCircleContour *self = (const GskCircleContour *) contour;
float t;
gsize idx;
if (self->radius == 0)
return 0;
idx = point->idx;
t = point->t;
if (self->ccw)
t = 1 - t;
{
idx = 5 - idx;
t = 1 - t;
}
return 2 * M_PI * self->radius * t;
return M_PI_2 * self->radius * (idx - 1 + t);
}
static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =

View File

@ -849,7 +849,7 @@ test_rect_in_fill (void)
}
static void
test_circle (void)
test_circle_basic (void)
{
GskPathBuilder *builder;
GskPath *path, *path1, *path2, *path3, *path4, *path5, *path6;
@ -1239,16 +1239,17 @@ test_rect_zero (void)
}
static void
check_rounded_rect_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)
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));
@ -1259,8 +1260,17 @@ check_rounded_rect_point (const GskPathPoint *point,
gsk_path_point_get_tangent (point, path, GSK_PATH_TO_END, &tan);
g_assert_true (graphene_vec2_near (tan2, &tan, 0.0001));
g_assert_cmpfloat_with_epsilon (k1, gsk_path_point_get_curvature (point, path, GSK_PATH_FROM_START, NULL), 0.0001);
g_assert_cmpfloat_with_epsilon (k2, gsk_path_point_get_curvature (point, path, GSK_PATH_TO_END, NULL), 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
@ -1306,11 +1316,11 @@ test_rounded_rect_plain (void)
point.contour = 0;
point.idx = 1;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (20, 10),
graphene_vec2_init (&v1, 1, 0),
graphene_vec2_init (&v2, 1, 0),
0.1, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (20, 10),
graphene_vec2_init (&v1, 1, 0),
graphene_vec2_init (&v2, 1, 0),
0.1, 0);
gsk_path_measure_get_point (measure, 80, &point);
gsk_path_point_get_position (&point, path, &pos);
@ -1319,11 +1329,11 @@ test_rounded_rect_plain (void)
point.contour = 0;
point.idx = 2;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (100, 10),
graphene_vec2_init (&v1, 1, 0),
graphene_vec2_init (&v2, 1, 0),
0, 0.1);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (100, 10),
graphene_vec2_init (&v1, 1, 0),
graphene_vec2_init (&v2, 1, 0),
0, 0.1);
gsk_path_measure_get_point (measure, 80 + (2 * M_PI * 10)/4, &point);
gsk_path_point_get_position (&point, path, &pos);
@ -1332,11 +1342,11 @@ test_rounded_rect_plain (void)
point.contour = 0;
point.idx = 3;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (110, 20),
graphene_vec2_init (&v1, 0, 1),
graphene_vec2_init (&v2, 0, 1),
0.1, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (110, 20),
graphene_vec2_init (&v1, 0, 1),
graphene_vec2_init (&v2, 0, 1),
0.1, 0);
gsk_path_measure_get_point (measure, 80 + (2 * M_PI * 10)/4 + 180, &point);
gsk_path_point_get_position (&point, path, &pos);
@ -1345,11 +1355,11 @@ test_rounded_rect_plain (void)
point.contour = 0;
point.idx = 4;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (110, 200),
graphene_vec2_init (&v1, 0, 1),
graphene_vec2_init (&v2, 0, 1),
0, 0.1);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (110, 200),
graphene_vec2_init (&v1, 0, 1),
graphene_vec2_init (&v2, 0, 1),
0, 0.1);
gsk_path_measure_get_point (measure, 80 + 180 + (2 * M_PI * 10)/2, &point);
gsk_path_point_get_position (&point, path, &pos);
@ -1358,11 +1368,11 @@ test_rounded_rect_plain (void)
point.contour = 0;
point.idx = 5;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (100, 210),
graphene_vec2_init (&v1, -1, 0),
graphene_vec2_init (&v2, -1, 0),
0.1, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (100, 210),
graphene_vec2_init (&v1, -1, 0),
graphene_vec2_init (&v2, -1, 0),
0.1, 0);
gsk_path_measure_get_point (measure, 80 + 180 + 80 + (2 * M_PI * 10)/2, &point);
gsk_path_point_get_position (&point, path, &pos);
@ -1371,11 +1381,11 @@ test_rounded_rect_plain (void)
point.contour = 0;
point.idx = 6;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (20, 210),
graphene_vec2_init (&v1, -1, 0),
graphene_vec2_init (&v2, -1, 0),
0, 0.1);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (20, 210),
graphene_vec2_init (&v1, -1, 0),
graphene_vec2_init (&v2, -1, 0),
0, 0.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);
@ -1384,11 +1394,11 @@ test_rounded_rect_plain (void)
point.contour = 0;
point.idx = 7;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (10, 200),
graphene_vec2_init (&v1, 0, -1),
graphene_vec2_init (&v2, 0, -1),
0.1, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (10, 200),
graphene_vec2_init (&v1, 0, -1),
graphene_vec2_init (&v2, 0, -1),
0.1, 0);
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);
@ -1397,11 +1407,11 @@ test_rounded_rect_plain (void)
point.contour = 0;
point.idx = 8;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (10, 20),
graphene_vec2_init (&v1, 0, -1),
graphene_vec2_init (&v2, 0, -1),
0, 0.1);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (10, 20),
graphene_vec2_init (&v1, 0, -1),
graphene_vec2_init (&v2, 0, -1),
0, 0.1);
gsk_path_measure_unref (measure);
gsk_path_unref (path);
@ -1450,65 +1460,165 @@ test_rounded_rect_tricky (void)
point.contour = 0;
point.idx = 1;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (10, 10),
graphene_vec2_init (&v1, 0, -1),
graphene_vec2_init (&v2, 1, 0),
0, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (10, 10),
graphene_vec2_init (&v1, 0, -1),
graphene_vec2_init (&v2, 1, 0),
0, 0);
point.contour = 0;
point.idx = 2;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (110, 10),
graphene_vec2_init (&v2, 1, 0),
graphene_vec2_init (&v1, 0, 1),
0, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (110, 10),
graphene_vec2_init (&v2, 1, 0),
graphene_vec2_init (&v1, 0, 1),
0, 0);
point.contour = 0;
point.idx = 3;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (110, 110),
graphene_vec2_init (&v1, 0, 1),
graphene_vec2_init (&v2, 0, 1),
0, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (110, 110),
graphene_vec2_init (&v1, 0, 1),
graphene_vec2_init (&v2, 0, 1),
0, 0);
point.contour = 0;
point.idx = 4;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (110, 210),
graphene_vec2_init (&v1, 0, 1),
graphene_vec2_init (&v2, -1, 0),
0, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (110, 210),
graphene_vec2_init (&v1, 0, 1),
graphene_vec2_init (&v2, -1, 0),
0, 0);
point.contour = 0;
point.idx = 5;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (20, 210),
graphene_vec2_init (&v1, -1, 0),
graphene_vec2_init (&v2, -1, 0),
0, 0.1);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (20, 210),
graphene_vec2_init (&v1, -1, 0),
graphene_vec2_init (&v2, -1, 0),
0, 0.1);
point.contour = 0;
point.idx = 6;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (10, 200),
graphene_vec2_init (&v2, 0, -1),
graphene_vec2_init (&v2, 0, -1),
0.1, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (10, 200),
graphene_vec2_init (&v2, 0, -1),
graphene_vec2_init (&v2, 0, -1),
0.1, 0);
point.contour = 0;
point.idx = 7;
point.t = 0;
check_rounded_rect_point (&point, path,
&GRAPHENE_POINT_INIT (10, 10),
graphene_vec2_init (&v1, 0, -1),
graphene_vec2_init (&v2, 1, 0),
0, 0);
check_path_point (&point, path,
&GRAPHENE_POINT_INIT (10, 10),
graphene_vec2_init (&v1, 0, -1),
graphene_vec2_init (&v2, 1, 0),
0, 0);
gsk_path_measure_unref (measure);
gsk_path_unref (path);
}
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 (100, 100);
radius = 10;
builder = gsk_path_builder_new ();
gsk_path_builder_add_circle (builder, &center, 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.70710678118654757 o -10 0, -10 -10, 0.70710678118654757 o 0 -10, 10 -10, 0.70710678118654757 o 10 0, 10 10, 0.70710678118654757 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.1, 0.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 (100, 100);
radius = 0;
builder = gsk_path_builder_new ();
gsk_path_builder_add_circle (builder, &center, 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.70710678118654757 o 0 0, 0 0, 0.70710678118654757 o 0 0, 0 0, 0.70710678118654757 o 0 0, 0 0, 0.70710678118654757 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, 0, 0);
check_path_point (&point, path,
&pos,
&v1, &v1,
INFINITY, INFINITY);
}
gsk_path_measure_unref (measure);
gsk_path_unref (path);
@ -1521,7 +1631,7 @@ main (int argc, char *argv[])
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/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);
@ -1531,16 +1641,18 @@ main (int argc, char *argv[])
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", test_circle);
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/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/circle/plain", test_circle_plain);
g_test_add_func ("/path/circle/zero", test_circle_zero);
return g_test_run ();
}