contour: Fixes for circles

Make circle contours use 'foreach coordinates' for
its points. This works here, but not for general
conics. As with the other custom contours, avoid
emitting collapsed conics.
This commit is contained in:
Matthias Clasen
2023-09-15 19:01:01 -04:00
parent 6d16776e27
commit 4d71ff6da1
2 changed files with 146 additions and 31 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

@@ -1514,6 +1514,52 @@ test_rounded_rect_tricky (void)
gsk_path_unref (path);
}
static void
test_circle_plain (void)
{
GskPathBuilder *builder;
GskPath *path;
GskPathMeasure *measure;
GskPathPoint point;
GskRoundedRect rect;
graphene_vec2_t v1, v2;
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);
angle = 2 * M_PI / 8;
gsk_path_measure_get_point (measure, angle * radius, &point);
pos = GRAPHENE_POINT_INIT (100 + cosf (angle), 100 + sinf (angle);
graphene_vec2_init (&v1, - sinf (angle), cosf (angle));
check_path_point (&point, path,
&pos,
graphene_vec2_init (&v1, - sinf (angle), ),
&v1, &v1,
0.1, 0.1);
gsk_path_measure_unref (measure);
gsk_path_unref (path);
}
int
main (int argc, char *argv[])
{
@@ -1541,6 +1587,7 @@ main (int argc, char *argv[])
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);
return g_test_run ();
}