From cf0afb785c2ecba53fc4b7357867c239227e2e71 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 19 Dec 2020 22:14:26 -0500 Subject: [PATCH] curve: Handle degenerate cases Nothing prevents control points from being identical, and if that happens, some of our constructions involving tangents and normals break down. Handle these cases in get_{start,end}_tangent and offset, for the case of cubics. --- gsk/gskcurve.c | 117 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 101 insertions(+), 16 deletions(-) diff --git a/gsk/gskcurve.c b/gsk/gskcurve.c index 0358e9d2f7..68882b3faf 100644 --- a/gsk/gskcurve.c +++ b/gsk/gskcurve.c @@ -979,7 +979,15 @@ gsk_cubic_curve_get_start_tangent (const GskCurve *curve, { const GskCubicCurve *self = &curve->cubic; - get_tangent (&self->points[0], &self->points[1], tangent); + if (graphene_point_near (&self->points[0], &self->points[1], 0.0001)) + { + if (graphene_point_near (&self->points[0], &self->points[2], 0.0001)) + get_tangent (&self->points[0], &self->points[3], tangent); + else + get_tangent (&self->points[0], &self->points[2], tangent); + } + else + get_tangent (&self->points[0], &self->points[1], tangent); } static void @@ -988,7 +996,15 @@ gsk_cubic_curve_get_end_tangent (const GskCurve *curve, { const GskCubicCurve *self = &curve->cubic; - get_tangent (&self->points[2], &self->points[3], tangent); + if (graphene_point_near (&self->points[2], &self->points[3], 0.0001)) + { + if (graphene_point_near (&self->points[1], &self->points[3], 0.0001)) + get_tangent (&self->points[0], &self->points[3], tangent); + else + get_tangent (&self->points[1], &self->points[3], tangent); + } + else + get_tangent (&self->points[2], &self->points[3], tangent); } static void @@ -1197,25 +1213,94 @@ gsk_cubic_curve_offset (const GskCurve *curve, graphene_vec2_t n; graphene_point_t p[4]; graphene_point_t m1, m2, m3, m4; + int coinc; - /* Simply scale control points, a la Tiller and Hanson */ - get_normal (&pts[0], &pts[1], &n); - scale_point (&pts[0], &n, distance, &p[0]); - scale_point (&pts[1], &n, distance, &m1); + coinc = (graphene_point_near (&pts[0], &pts[1], 0.001) << 0) | + (graphene_point_near (&pts[1], &pts[2], 0.001) << 1) | + (graphene_point_near (&pts[2], &pts[3], 0.001) << 2); - get_normal (&pts[1], &pts[2], &n); - scale_point (&pts[1], &n, distance, &m2); - scale_point (&pts[2], &n, distance, &m3); + if (coinc == 7) + { + /* just give up */ + p[0] = pts[0]; + p[1] = pts[1]; + p[2] = pts[2]; + p[3] = pts[3]; + } + else if (coinc == 3 || coinc == 5 || coinc == 6) + { + /* a straight line */ + get_normal (&pts[0], &pts[3], &n); + scale_point (&pts[0], &n, distance, &p[0]); + scale_point (&pts[3], &n, distance, &p[3]); - get_normal (&pts[2], &pts[3], &n); - scale_point (&pts[2], &n, distance, &m4); - scale_point (&pts[3], &n, distance, &p[3]); + if (coinc & (1 << 0)) + p[1] = p[0]; + else + p[1] = p[3]; + if (coinc & (1 << 2)) + p[2] = p[3]; + else + p[2] = p[0]; + } + else if (coinc == 1 || coinc == 2 || coinc == 4) + { + graphene_point_t p1; - if (!line_intersection (&p[0], &m1, &m2, &m3, &p[1])) - p[1] = m1; + if (coinc == 1) + p1 = pts[2]; + else + p1 = pts[1]; - if (!line_intersection (&m2, &m3, &m4, &p[3], &p[2])) - p[2] = m4; + get_normal (&pts[0], &p1, &n); + scale_point (&pts[0], &n, distance, &p[0]); + scale_point (&p1, &n, distance, &m1); + + get_normal (&p1, &pts[3], &n); + scale_point (&p1, &n, distance, &m2); + scale_point (&pts[3], &n, distance, &p[3]); + + if (!line_intersection (&p[0], &m1, &m2, &p[3], &m3)) + m3 = m1; + + if (coinc == 1) + { + p[1] = p[0]; + p[2] = m3; + } + else if (coinc == 2) + { + p[1] = m3; + p[2] = m3; + } + else + { + p[1] = m3; + p[2] = p[3]; + } + } + else if (coinc == 0) + { + get_normal (&pts[0], &pts[1], &n); + scale_point (&pts[0], &n, distance, &p[0]); + scale_point (&pts[1], &n, distance, &m1); + + get_normal (&pts[1], &pts[2], &n); + scale_point (&pts[1], &n, distance, &m2); + scale_point (&pts[2], &n, distance, &m3); + + get_normal (&pts[2], &pts[3], &n); + scale_point (&pts[2], &n, distance, &m4); + scale_point (&pts[3], &n, distance, &p[3]); + + if (graphene_point_near (&m1, &m2, 0.01) || + !line_intersection (&p[0], &m1, &m2, &m3, &p[1])) + p[1] = m1; + + if (graphene_point_near (&m3, &m4, 0.01) || + !line_intersection (&m2, &m3, &m4, &p[3], &p[2])) + p[2] = m4; + } gsk_cubic_curve_init_from_points (&offset->cubic, p); }