[gtk/path-work-rebased: 98/121] curve: Handle degenerate cases




commit ad145e201bf2d3fa4effd73753ca7494ec5576bb
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Dec 19 22:14:26 2020 -0500

    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 | 115 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 99 insertions(+), 16 deletions(-)
---
diff --git a/gsk/gskcurve.c b/gsk/gskcurve.c
index b4a43ad497..db997b10e1 100644
--- a/gsk/gskcurve.c
+++ b/gsk/gskcurve.c
@@ -558,7 +558,15 @@ gsk_curve_curve_get_start_tangent (const GskCurve  *curve,
 {
   const GskCurveCurve *self = &curve->curve;
 
-  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
@@ -567,7 +575,15 @@ gsk_curve_curve_get_end_tangent (const GskCurve  *curve,
 {
   const GskCurveCurve *self = &curve->curve;
 
-  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
@@ -667,25 +683,92 @@ gsk_curve_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];
+
+      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 (!line_intersection (&m2, &m3, &m4, &p[3], &p[2]))
-    p[2] = m4;
+      if (!line_intersection (&p[0], &m1, &m2, &m3, &p[1]))
+        p[1] = m1;
+
+      if (!line_intersection (&m2, &m3, &m4, &p[3], &p[2]))
+        p[2] = m4;
+    }
 
   gsk_curve_curve_init_from_points (&offset->curve, p);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]