diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 95793851cc..9d12e18595 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -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 = diff --git a/testsuite/gsk/path-special-cases.c b/testsuite/gsk/path-special-cases.c index 465217270c..f773a53c85 100644 --- a/testsuite/gsk/path-special-cases.c +++ b/testsuite/gsk/path-special-cases.c @@ -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, ¢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.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 (); }