[libshumate] vector: Avoid sharp angles in line symbols



commit 95e36975cd062a7150f03f5faeb0c1263832d1e4
Author: James Westman <james jwestman net>
Date:   Tue May 17 17:07:10 2022 -0500

    vector: Avoid sharp angles in line symbols

 .../vector/shumate-vector-render-scope-private.h   |  3 +-
 shumate/vector/shumate-vector-render-scope.c       | 63 +++++++++++------
 shumate/vector/shumate-vector-symbol-layer.c       | 82 ++++++++++++++--------
 shumate/vector/shumate-vector-utils-private.h      |  2 +-
 shumate/vector/shumate-vector-utils.c              | 43 +++++++++++-
 5 files changed, 136 insertions(+), 57 deletions(-)
---
diff --git a/shumate/vector/shumate-vector-render-scope-private.h 
b/shumate/vector/shumate-vector-render-scope-private.h
index d53f3cb..eb121b3 100644
--- a/shumate/vector/shumate-vector-render-scope-private.h
+++ b/shumate/vector/shumate-vector-render-scope-private.h
@@ -46,7 +46,6 @@ void shumate_vector_render_scope_get_bounds (ShumateVectorRenderScope *self,
                                              double                   *max_x,
                                              double                   *max_y);
 
-void shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self,
-                                               ShumateVectorLineString  *linestring);
+GPtrArray *shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self);
 
 void shumate_vector_render_scope_get_variable (ShumateVectorRenderScope *self, const char *variable, 
ShumateVectorValue *value);
diff --git a/shumate/vector/shumate-vector-render-scope.c b/shumate/vector/shumate-vector-render-scope.c
index 8b9106b..ca905f2 100644
--- a/shumate/vector/shumate-vector-render-scope.c
+++ b/shumate/vector/shumate-vector-render-scope.c
@@ -17,6 +17,12 @@
 
 #include "shumate-vector-render-scope-private.h"
 
+enum {
+  MOVE_TO = 1,
+  LINE_TO = 2,
+  CLOSE_PATH = 7,
+};
+
 /* Sets the current layer by name. */
 gboolean
 shumate_vector_render_scope_find_layer (ShumateVectorRenderScope *self, const char *layer_name)
@@ -62,19 +68,19 @@ shumate_vector_render_scope_exec_geometry (ShumateVectorRenderScope *self)
       for (int j = 0; j < repeat; j ++)
         {
           switch (op) {
-          case 1:
+          case MOVE_TO:
             g_return_if_fail (i + 2 < self->feature->n_geometry);
             dx = zigzag (self->feature->geometry[++i]);
             dy = zigzag (self->feature->geometry[++i]);
             cairo_rel_move_to (self->cr, dx, dy);
             break;
-          case 2:
+          case LINE_TO:
             g_return_if_fail (i + 2 < self->feature->n_geometry);
             dx = zigzag (self->feature->geometry[++i]);
             dy = zigzag (self->feature->geometry[++i]);
             cairo_rel_line_to (self->cr, dx, dy);
             break;
-          case 7:
+          case CLOSE_PATH:
             cairo_get_current_point (self->cr, &dx, &dy);
             cairo_close_path (self->cr);
             cairo_move_to (self->cr, dx, dy);
@@ -87,15 +93,14 @@ shumate_vector_render_scope_exec_geometry (ShumateVectorRenderScope *self)
 }
 
 
-void
-shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self,
-                                          ShumateVectorLineString  *linestring)
+GPtrArray *
+shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self)
 {
-  ShumateVectorPoint *points = NULL;
-  gsize point_index = 0;
+  GPtrArray *lines = g_ptr_array_new ();
+  ShumateVectorLineString *current_line = NULL;
   double x = 0, y = 0;
 
-  g_return_if_fail (self->feature != NULL);
+  g_return_val_if_fail (self->feature != NULL, NULL);
 
   for (int i = 0; i < self->feature->n_geometry; i ++)
     {
@@ -105,34 +110,47 @@ shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self,
       int op = cmd & 0x7;
       int repeat = cmd >> 3;
 
-      points = g_realloc (points, (repeat + point_index) * sizeof (ShumateVectorPoint));
-      g_return_if_fail (points != NULL);
+      if (current_line != NULL)
+        current_line->points = g_realloc (current_line->points, (repeat + current_line->n_points) * sizeof 
(ShumateVectorPoint));
 
       for (int j = 0; j < repeat; j ++)
         {
           switch (op) {
-          case 1:
-            g_return_if_fail (i + 2 < self->feature->n_geometry);
+          case MOVE_TO:
+            g_return_val_if_fail (i + 2 < self->feature->n_geometry, NULL);
+
+            if (current_line != NULL)
+              g_ptr_array_add (lines, current_line);
+
+            current_line = g_new (ShumateVectorLineString, 1);
+            current_line->points = g_new (ShumateVectorPoint, 1);
+            current_line->n_points = 1;
+
             x += zigzag (self->feature->geometry[++i]);
             y += zigzag (self->feature->geometry[++i]);
             start_x = x;
             start_y = y;
-            points[point_index++] = (ShumateVectorPoint) {
+
+            current_line->points[0] = (ShumateVectorPoint) {
               .x = x / self->layer->extent,
               .y = y / self->layer->extent,
             };
             break;
-          case 2:
-            g_return_if_fail (i + 2 < self->feature->n_geometry);
+          case LINE_TO:
+            g_return_val_if_fail (i + 2 < self->feature->n_geometry, NULL);
+            g_return_val_if_fail (current_line != NULL, NULL);
+
             x += zigzag (self->feature->geometry[++i]);
             y += zigzag (self->feature->geometry[++i]);
-            points[point_index++] = (ShumateVectorPoint) {
+            current_line->points[current_line->n_points++] = (ShumateVectorPoint) {
               .x = x / self->layer->extent,
               .y = y / self->layer->extent,
             };
             break;
-          case 7:
-            points[point_index++] = (ShumateVectorPoint) {
+          case CLOSE_PATH:
+            g_return_val_if_fail (current_line != NULL, NULL);
+
+            current_line->points[current_line->n_points++] = (ShumateVectorPoint) {
               .x = start_x / self->layer->extent,
               .y = start_y / self->layer->extent,
             };
@@ -143,8 +161,10 @@ shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self,
         }
     }
 
-  linestring->n_points = point_index;
-  linestring->points = points;
+  if (current_line != NULL)
+    g_ptr_array_add (lines, current_line);
+
+  return lines;
 }
 
 
@@ -265,3 +285,4 @@ shumate_vector_render_scope_get_variable (ShumateVectorRenderScope *self, const
         }
     }
 }
+
diff --git a/shumate/vector/shumate-vector-symbol-layer.c b/shumate/vector/shumate-vector-symbol-layer.c
index 82a28d5..af5a253 100644
--- a/shumate/vector/shumate-vector-symbol-layer.c
+++ b/shumate/vector/shumate-vector-symbol-layer.c
@@ -126,47 +126,69 @@ shumate_vector_symbol_layer_render (ShumateVectorLayer *layer, ShumateVectorRend
   if (strlen (text_field) == 0)
     return;
 
-  symbol_info = shumate_vector_symbol_info_new (text_field,
-                                                &text_color,
-                                                text_size,
-                                                self->text_fonts,
-                                                self->line_placement,
-                                                x,
-                                                y);
-
   if (self->line_placement)
     {
-      ShumateVectorLineString linestring;
-      shumate_vector_render_scope_get_geometry (scope, &linestring);
-      shumate_vector_line_string_simplify (&linestring);
-      shumate_vector_symbol_info_set_line_points (symbol_info, &linestring);
+      g_autoptr(GPtrArray) lines = shumate_vector_render_scope_get_geometry (scope);
+      guint i, j;
+
+      for (i = 0; i < lines->len; i ++)
+        {
+          g_autoptr(GPtrArray) split_lines = shumate_vector_line_string_simplify ((ShumateVectorLineString 
*)lines->pdata[i]);
+
+          for (j = 0; j < split_lines->len; j ++)
+            {
+              ShumateVectorLineString *linestring = (ShumateVectorLineString *)split_lines->pdata[j];
+              shumate_vector_line_string_simplify (linestring);
 
 #if 0
-      /* visualize line simplification */
+              /* visualize line simplification */
 
-      line_points = shumate_vector_render_scope_get_geometry (scope, &num_points);
-      shumate_vector_line_simplify (line_points, &num_points);
+              if (linestring->n_points > 0)
+                {
+                  guint k;
+                  float scale = scope->scale * scope->target_size;
 
-      if (num_points > 0)
-        {
-          float scale = scope->scale * scope->target_size;
+                  cairo_set_source_rgb (scope->cr,
+                                        rand () % 255 / 255.0,
+                                        rand () % 255 / 255.0,
+                                        rand () % 255 / 255.0);
+                  cairo_set_line_width (scope->cr, scope->scale);
+
+                  cairo_move_to (scope->cr, linestring->points[0].x * scale, linestring->points[0].y * 
scale);
+                  for (k = 1; k < linestring->n_points; k ++)
+                    cairo_line_to (scope->cr, linestring->points[k].x * scale, linestring->points[k].y * 
scale);
 
-          cairo_set_source_rgb (scope->cr,
-                                rand () % 255 / 255.0,
-                                rand () % 255 / 255.0,
-                                rand () % 255 / 255.0);
-          cairo_set_line_width (scope->cr, scope->scale);
+                  cairo_stroke (scope->cr);
+                }
+#endif
+
+              symbol_info = shumate_vector_symbol_info_new (text_field,
+                                                            &text_color,
+                                                            text_size,
+                                                            self->text_fonts,
+                                                            TRUE,
+                                                            x,
+                                                            y);
 
-          cairo_move_to (scope->cr, line_points[0].x * scale, line_points[0].y * scale);
-          for (gsize i = 1; i < num_points; i ++)
-            cairo_line_to (scope->cr, line_points[i].x * scale, line_points[i].y * scale);
+              shumate_vector_symbol_info_set_line_points (symbol_info,
+                                                          linestring);
 
-          cairo_stroke (scope->cr);
+              g_ptr_array_add (scope->symbols, symbol_info);
+            }
         }
-#endif
     }
-
-  g_ptr_array_add (scope->symbols, symbol_info);
+  else
+    {
+      symbol_info = shumate_vector_symbol_info_new (text_field,
+                                                    &text_color,
+                                                    text_size,
+                                                    self->text_fonts,
+                                                    FALSE,
+                                                    x,
+                                                    y);
+
+      g_ptr_array_add (scope->symbols, symbol_info);
+    }
 }
 
 
diff --git a/shumate/vector/shumate-vector-utils-private.h b/shumate/vector/shumate-vector-utils-private.h
index df26269..712992c 100644
--- a/shumate/vector/shumate-vector-utils-private.h
+++ b/shumate/vector/shumate-vector-utils-private.h
@@ -96,4 +96,4 @@ double shumate_vector_line_string_length              (ShumateVectorLineString *
 void   shumate_vector_line_string_bounds              (ShumateVectorLineString *linestring,
                                                        ShumateVectorPoint      *radius_out,
                                                        ShumateVectorPoint      *center_out);
-void   shumate_vector_line_string_simplify            (ShumateVectorLineString *linestring);
+GPtrArray *shumate_vector_line_string_simplify        (ShumateVectorLineString *linestring);
diff --git a/shumate/vector/shumate-vector-utils.c b/shumate/vector/shumate-vector-utils.c
index f76e314..09596b9 100644
--- a/shumate/vector/shumate-vector-utils.c
+++ b/shumate/vector/shumate-vector-utils.c
@@ -369,13 +369,16 @@ shumate_vector_line_string_bounds (ShumateVectorLineString *linestring,
 }
 
 
-void
+GPtrArray *
 shumate_vector_line_string_simplify (ShumateVectorLineString *linestring)
 {
-  gint i;
+  gsize i;
+  GPtrArray *result = g_ptr_array_new ();
+
+  g_ptr_array_add (result, linestring);
 
   if (linestring->n_points <= 2)
-    return;
+    return result;
 
   /* The glyph layout algorithm for line symbols does not handle high detail
    * very well. Lots of short segments with different angles cause it to place
@@ -428,4 +431,38 @@ shumate_vector_line_string_simplify (ShumateVectorLineString *linestring)
       for (i = min_idx + 1; i < linestring->n_points; i ++)
         linestring->points[i] = linestring->points[i + 1];
     }
+
+  /* Line labels also don't look good if there's sharp angles. To fix that, we
+   * split the line wherever one occurs. */
+
+  for (i = linestring->n_points - 2; i >= 1; i --)
+    {
+      /* Angle between three points. See https://math.stackexchange.com/a/3427603 */
+
+      /* Dot product of points[i] to points[i - 1], and points[i] to points[i + 1] */
+      float dot = (linestring->points[i].x - linestring->points[i + 1].x)
+                    * (linestring->points[i].x - linestring->points[i - 1].x)
+                  + ((linestring->points[i].y - linestring->points[i + 1].y)
+                    * (linestring->points[i].y - linestring->points[i - 1].y));
+
+      float len = sqrt (point_distance_sq (&linestring->points[i], &linestring->points[i + 1])
+                        * point_distance_sq (&linestring->points[i], &linestring->points[i - 1]));
+
+      float angle = fabs (acos (dot / len));
+
+      if (angle < 120 * G_PI / 180)
+        {
+          ShumateVectorLineString *new_line = g_new (ShumateVectorLineString, 1);
+
+          /* Copy from the current point until the end of the line */
+          new_line->n_points = linestring->n_points - i;
+          new_line->points = g_memdup2 (&linestring->points[i], new_line->n_points * sizeof 
(ShumateVectorPoint));
+
+          linestring->n_points = i + 1;
+
+          g_ptr_array_add (result, new_line);
+        }
+    }
+
+  return result;
 }


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