[gtk/curve-ops: 11/12] Add gsk_curve_offset




commit 3b4d77af1cd0618442a9e5285ad633e813648cde
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Dec 10 18:19:28 2020 -0500

    Add gsk_curve_offset
    
    This method creates an offset curve from an existing
    curve by just moving the control points laterally.
    
    This will be used in stroking.

 gsk/gskcurve.c        | 152 +++++++++++++++++++++++++++++++++++++++++++++++++-
 gsk/gskcurveprivate.h |   4 ++
 2 files changed, 153 insertions(+), 3 deletions(-)
---
diff --git a/gsk/gskcurve.c b/gsk/gskcurve.c
index d1f6c235c2..775a3c6da4 100644
--- a/gsk/gskcurve.c
+++ b/gsk/gskcurve.c
@@ -67,6 +67,9 @@ struct _GskCurveClass
                                                          graphene_rect_t        *bounds);
   void                          (* get_tight_bounds)    (const GskCurve         *curve,
                                                          graphene_rect_t        *bounds);
+  void                          (* offset)              (const GskCurve         *curve,
+                                                         float                   distance,
+                                                         GskCurve               *offset_curve);
 };
 
 static void
@@ -78,6 +81,60 @@ get_tangent (const graphene_point_t *p0,
   graphene_vec2_normalize (t, t);
 }
 
+static void
+get_normal (const graphene_point_t *p0,
+            const graphene_point_t *p1,
+            graphene_vec2_t        *n)
+{
+  graphene_vec2_init (n, p0->y - p1->y, p1->x - p0->x);
+  graphene_vec2_normalize (n, n);
+}
+
+/* Compute q = p + d * n */
+static void
+scale_point (const graphene_point_t *p,
+             const graphene_vec2_t  *n,
+             float                   d,
+             graphene_point_t       *q)
+{
+  q->x = p->x + d * graphene_vec2_get_x (n);
+  q->y = p->y + d * graphene_vec2_get_y (n);
+}
+
+/* Set p to the intersection of the lines through a, b and c, d.
+ * Return the number of intersections found (0 or 1)
+ */
+static int
+line_intersection (const graphene_point_t *a,
+                   const graphene_point_t *b,
+                   const graphene_point_t *c,
+                   const graphene_point_t *d,
+                   graphene_point_t       *p)
+{
+  float a1 = b->y - a->y;
+  float b1 = a->x - b->x;
+  float c1 = a1*a->x + b1*a->y;
+
+  float a2 = d->y - c->y;
+  float b2 = c->x - d->x;
+  float c2 = a2*c->x+ b2*c->y;
+
+  float det = a1*b2 - a2*b1;
+
+  if (fabs (det) < 0.001)
+    {
+      p->x = NAN;
+      p->y = NAN;
+      return 0;
+    }
+  else
+    {
+      p->x = (b2*c1 - b1*c2) / det;
+      p->y = (a1*c2 - a2*c1) / det;
+      return 1;
+    }
+}
+
 /** LINE **/
 
 static void
@@ -239,6 +296,23 @@ gsk_line_curve_get_bounds (const GskCurve  *curve,
   graphene_rect_expand (bounds, &pts[1], bounds);
 }
 
+static void
+gsk_line_curve_offset (const GskCurve *curve,
+                       float           distance,
+                       GskCurve       *offset_curve)
+{
+  const GskLineCurve *self = &curve->line;
+  const graphene_point_t *pts = self->points;
+  graphene_vec2_t n;
+  graphene_point_t p[4];
+
+  get_normal (&pts[0], &pts[1], &n);
+  scale_point (&pts[0], &n, distance, &p[0]);
+  scale_point (&pts[1], &n, distance, &p[1]);
+
+  gsk_curve_init (offset_curve, gsk_pathop_encode (GSK_PATH_LINE, p));
+}
+
 static const GskCurveClass GSK_LINE_CURVE_CLASS = {
   gsk_line_curve_init,
   gsk_line_curve_init_foreach,
@@ -254,7 +328,8 @@ static const GskCurveClass GSK_LINE_CURVE_CLASS = {
   gsk_line_curve_get_start_end_tangent,
   gsk_line_curve_get_start_end_tangent,
   gsk_line_curve_get_bounds,
-  gsk_line_curve_get_bounds
+  gsk_line_curve_get_bounds,
+  gsk_line_curve_offset
 };
 
 /** CURVE **/
@@ -568,6 +643,39 @@ gsk_curve_curve_get_tight_bounds (const GskCurve  *curve,
     }
 }
 
+static void
+gsk_curve_curve_offset (const GskCurve *curve,
+                        float           distance,
+                        GskCurve       *offset)
+{
+  const GskCurveCurve *self = &curve->curve;
+  const graphene_point_t *pts = self->points;
+  graphene_vec2_t n;
+  graphene_point_t p[4];
+  graphene_point_t m1, m2, m3, m4;
+
+  /* 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);
+
+  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 (&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);
+}
+
 static const GskCurveClass GSK_CURVE_CURVE_CLASS = {
   gsk_curve_curve_init,
   gsk_curve_curve_init_foreach,
@@ -583,7 +691,8 @@ static const GskCurveClass GSK_CURVE_CURVE_CLASS = {
   gsk_curve_curve_get_start_tangent,
   gsk_curve_curve_get_end_tangent,
   gsk_curve_curve_get_bounds,
-  gsk_curve_curve_get_tight_bounds
+  gsk_curve_curve_get_tight_bounds,
+  gsk_curve_curve_offset
 };
 
 /** CONIC **/
@@ -1148,6 +1257,34 @@ gsk_conic_curve_get_tight_bounds (const GskCurve  *curve,
     }
 }
 
+static void
+gsk_conic_curve_offset (const GskCurve *curve,
+                        float           distance,
+                        GskCurve       *offset)
+{
+  const GskConicCurve *self = &curve->conic;
+  const graphene_point_t *pts = self->points;
+  graphene_vec2_t n;
+  graphene_point_t p[4];
+  graphene_point_t m1, m2;
+
+  /* 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);
+
+  get_normal (&pts[1], &pts[3], &n);
+  scale_point (&pts[1], &n, distance, &m2);
+  scale_point (&pts[3], &n, distance, &p[3]);
+
+  if (!line_intersection (&p[0], &m1, &m2, &p[3], &p[1]))
+    p[1] = m1;
+
+  p[2] = pts[2];
+
+  gsk_conic_curve_init_from_points (&offset->conic, p);
+}
+
 static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
   gsk_conic_curve_init,
   gsk_conic_curve_init_foreach,
@@ -1163,7 +1300,8 @@ static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
   gsk_conic_curve_get_start_tangent,
   gsk_conic_curve_get_end_tangent,
   gsk_conic_curve_get_bounds,
-  gsk_conic_curve_get_tight_bounds
+  gsk_conic_curve_get_tight_bounds,
+  gsk_conic_curve_offset
 };
 
 /** API **/
@@ -1303,3 +1441,11 @@ gsk_curve_get_tight_bounds (const GskCurve  *curve,
 {
   get_class (curve->op)->get_tight_bounds (curve, bounds);
 }
+
+void
+gsk_curve_offset (const GskCurve *curve,
+                  float           distance,
+                  GskCurve       *offset_curve)
+{
+  get_class (curve->op)->offset (curve, distance, offset_curve);
+}
diff --git a/gsk/gskcurveprivate.h b/gsk/gskcurveprivate.h
index 1ae20e9e44..e4705f915b 100644
--- a/gsk/gskcurveprivate.h
+++ b/gsk/gskcurveprivate.h
@@ -132,6 +132,10 @@ int                     gsk_curve_intersect                     (const GskCurve
                                                                  graphene_point_t       *p,
                                                                  int                     n);
 
+void                    gsk_curve_offset                        (const GskCurve         *curve,
+                                                                 float                   distance,
+                                                                 GskCurve               *offset_curve);
+
 G_END_DECLS
 
 #endif /* __GSK_CURVE_PRIVATE_H__ */


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