[libshumate] vector: Line symbols



commit cc062e586338e6e53847f15e7b0b27994291a52c
Author: James Westman <james jwestman net>
Date:   Fri Feb 4 10:08:48 2022 -0600

    vector: Line symbols

 demos/map-style.json                               |  14 ++
 .../vector/shumate-vector-render-scope-private.h   |   4 +
 shumate/vector/shumate-vector-render-scope.c       |  61 ++++++++
 .../shumate-vector-symbol-container-private.h      |   1 +
 shumate/vector/shumate-vector-symbol-container.c   |  14 +-
 .../vector/shumate-vector-symbol-info-private.h    |   8 +-
 shumate/vector/shumate-vector-symbol-info.c        |   9 ++
 shumate/vector/shumate-vector-symbol-layer.c       |   8 ++
 shumate/vector/shumate-vector-symbol.c             | 142 +++++++++++++++++-
 shumate/vector/shumate-vector-utils-private.h      |  36 ++++-
 shumate/vector/shumate-vector-utils.c              | 160 +++++++++++++++++++++
 11 files changed, 449 insertions(+), 8 deletions(-)
---
diff --git a/demos/map-style.json b/demos/map-style.json
index 08ef218..2212534 100644
--- a/demos/map-style.json
+++ b/demos/map-style.json
@@ -64,6 +64,20 @@
       "paint": {
         "text-color": "#000000"
       }
+    },
+    {
+      "id": "waterway",
+      "type": "symbol",
+      "source-layer": "waterway",
+      "layout": {
+        "text-field": "{name}",
+        "symbol-placement": "line",
+        "font-family": "Cantarell 16"
+      },
+      "paint": {
+        "text-color": "#000000"
+      }
     }
   ]
 }
+
diff --git a/shumate/vector/shumate-vector-render-scope-private.h 
b/shumate/vector/shumate-vector-render-scope-private.h
index ac728a0..d53f3cb 100644
--- a/shumate/vector/shumate-vector-render-scope-private.h
+++ b/shumate/vector/shumate-vector-render-scope-private.h
@@ -21,6 +21,7 @@
 #include <cairo/cairo.h>
 #include "vector_tile.pb-c.h"
 #include "shumate-vector-value-private.h"
+#include "shumate-vector-utils-private.h"
 
 typedef struct {
   cairo_t *cr;
@@ -45,4 +46,7 @@ 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);
+
 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 14e7644..3a156d6 100644
--- a/shumate/vector/shumate-vector-render-scope.c
+++ b/shumate/vector/shumate-vector-render-scope.c
@@ -87,6 +87,67 @@ shumate_vector_render_scope_exec_geometry (ShumateVectorRenderScope *self)
 }
 
 
+void
+shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self,
+                                          ShumateVectorLineString  *linestring)
+{
+  ShumateVectorPoint *points = NULL;
+  gsize point_index = 0;
+  double x = 0, y = 0;
+
+  g_return_if_fail (self->feature != NULL);
+
+  for (int i = 0; i < self->feature->n_geometry; i ++)
+    {
+      int cmd = self->feature->geometry[i];
+      double start_x = 0, start_y = 0;
+
+      int op = cmd & 0x7;
+      int repeat = cmd >> 3;
+
+      points = g_realloc (points, (repeat + point_index) * sizeof (ShumateVectorPoint));
+      g_return_if_fail (points != NULL);
+
+      for (int j = 0; j < repeat; j ++)
+        {
+          switch (op) {
+          case 1:
+            g_return_if_fail (i + 2 < self->feature->n_geometry);
+            x += zigzag (self->feature->geometry[++i]);
+            y += zigzag (self->feature->geometry[++i]);
+            start_x = x;
+            start_y = y;
+            points[point_index++] = (ShumateVectorPoint) {
+              .x = x / self->layer->extent,
+              .y = y / self->layer->extent,
+            };
+            break;
+          case 2:
+            g_return_if_fail (i + 2 < self->feature->n_geometry);
+            x += zigzag (self->feature->geometry[++i]);
+            y += zigzag (self->feature->geometry[++i]);
+            points[point_index++] = (ShumateVectorPoint) {
+              .x = x / self->layer->extent,
+              .y = y / self->layer->extent,
+            };
+            break;
+          case 7:
+            points[point_index++] = (ShumateVectorPoint) {
+              .x = start_x / self->layer->extent,
+              .y = start_y / self->layer->extent,
+            };
+            break;
+          default:
+            g_assert_not_reached ();
+          }
+        }
+    }
+
+  linestring->n_points = point_index;
+  linestring->points = points;
+}
+
+
 void
 shumate_vector_render_scope_get_bounds (ShumateVectorRenderScope *self,
                                         double                   *min_x,
diff --git a/shumate/vector/shumate-vector-symbol-container-private.h 
b/shumate/vector/shumate-vector-symbol-container-private.h
index 33f752e..c86a507 100644
--- a/shumate/vector/shumate-vector-symbol-container-private.h
+++ b/shumate/vector/shumate-vector-symbol-container-private.h
@@ -40,4 +40,5 @@ void shumate_vector_symbol_container_remove_symbols (ShumateVectorSymbolContaine
                                                      int                           tile_y,
                                                      int                           zoom);
 
+ShumateMapSource *shumate_vector_symbol_container_get_map_source (ShumateVectorSymbolContainer *self);
 G_END_DECLS
diff --git a/shumate/vector/shumate-vector-symbol-container.c 
b/shumate/vector/shumate-vector-symbol-container.c
index c2681e5..c10aeb6 100644
--- a/shumate/vector/shumate-vector-symbol-container.c
+++ b/shumate/vector/shumate-vector-symbol-container.c
@@ -42,8 +42,9 @@ static GParamSpec *obj_properties[N_PROPS] = { NULL, };
 
 
 typedef struct {
-  // does not need to be freed because it's owned by the widget
+  // do not need to be freed because they're owned by the widget
   ShumateVectorSymbol *symbol;
+  ShumateVectorSymbolInfo *symbol_info;
 
   ShumateVectorCollisionMarker *marker;
 
@@ -223,6 +224,8 @@ shumate_vector_symbol_container_size_allocate (GtkWidget *widget,
       alloc.height = child->height;
 
       gtk_widget_size_allocate (GTK_WIDGET (child->symbol), &alloc, -1);
+      if (child->symbol_info->line_placement)
+        gtk_widget_queue_draw (GTK_WIDGET (child->symbol));
     }
 }
 
@@ -297,6 +300,7 @@ shumate_vector_symbol_container_add_symbols (ShumateVectorSymbolContainer *self,
       ShumateVectorSymbol *symbol = shumate_vector_symbol_new (symbol_info);
 
       info->symbol = symbol;
+      info->symbol_info = symbol_info;
       gtk_widget_measure (GTK_WIDGET (symbol), GTK_ORIENTATION_HORIZONTAL, -1, NULL, &info->width, NULL, 
NULL);
       gtk_widget_measure (GTK_WIDGET (symbol), GTK_ORIENTATION_VERTICAL, -1, NULL, &info->height, NULL, 
NULL);
       info->x = symbol_info->x;
@@ -343,3 +347,11 @@ shumate_vector_symbol_container_remove_symbols (ShumateVectorSymbolContainer *se
 
   self->children = g_list_remove_all (self->children, NULL);
 }
+
+
+ShumateMapSource *
+shumate_vector_symbol_container_get_map_source (ShumateVectorSymbolContainer *self)
+{
+  g_return_val_if_fail (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (self), NULL);
+  return self->map_source;
+}
diff --git a/shumate/vector/shumate-vector-symbol-info-private.h 
b/shumate/vector/shumate-vector-symbol-info-private.h
index 3552f8f..bfe19cd 100644
--- a/shumate/vector/shumate-vector-symbol-info-private.h
+++ b/shumate/vector/shumate-vector-symbol-info-private.h
@@ -19,6 +19,7 @@
 #pragma once
 
 #include <glib-object.h>
+#include "shumate-vector-utils-private.h"
 #include "shumate-vector-render-scope-private.h"
 
 
@@ -34,10 +35,12 @@ struct _ShumateVectorSymbolInfo
   GdkRGBA text_color;
   double text_size;
   char *text_font;
-  gboolean line_placement;
+  guint line_placement : 1;
   double x;
   double y;
 
+  ShumateVectorLineString line;
+
   /*< private >*/
   guint ref_count;
 };
@@ -50,6 +53,9 @@ ShumateVectorSymbolInfo *shumate_vector_symbol_info_new (const char
                                                          double                    x,
                                                          double                    y);
 
+void shumate_vector_symbol_info_set_line_points (ShumateVectorSymbolInfo *self,
+                                                 ShumateVectorLineString *linestring);
+
 GType                        shumate_vector_symbol_info_get_type (void) G_GNUC_CONST;
 ShumateVectorSymbolInfo     *shumate_vector_symbol_info_ref      (ShumateVectorSymbolInfo *self);
 void                         shumate_vector_symbol_info_unref    (ShumateVectorSymbolInfo *self);
diff --git a/shumate/vector/shumate-vector-symbol-info.c b/shumate/vector/shumate-vector-symbol-info.c
index de8a605..8731b76 100644
--- a/shumate/vector/shumate-vector-symbol-info.c
+++ b/shumate/vector/shumate-vector-symbol-info.c
@@ -30,6 +30,7 @@ shumate_vector_symbol_info_free (ShumateVectorSymbolInfo *self)
 
   g_clear_pointer (&self->text, g_free);
   g_clear_pointer (&self->text_font, g_free);
+  shumate_vector_line_string_clear (&self->line);
 
   g_free (self);
 }
@@ -83,3 +84,11 @@ shumate_vector_symbol_info_new (const char    *text,
   return self;
 }
 
+
+void
+shumate_vector_symbol_info_set_line_points (ShumateVectorSymbolInfo *self,
+                                            ShumateVectorLineString *linestring)
+{
+  shumate_vector_line_string_clear (&self->line);
+  self->line = *linestring;
+}
diff --git a/shumate/vector/shumate-vector-symbol-layer.c b/shumate/vector/shumate-vector-symbol-layer.c
index d57589d..a73fba9 100644
--- a/shumate/vector/shumate-vector-symbol-layer.c
+++ b/shumate/vector/shumate-vector-symbol-layer.c
@@ -130,6 +130,14 @@ shumate_vector_symbol_layer_render (ShumateVectorLayer *layer, ShumateVectorRend
                                                 self->line_placement,
                                                 x,
                                                 y);
+
+  if (self->line_placement)
+    {
+      ShumateVectorLineString linestring;
+      shumate_vector_render_scope_get_geometry (scope, &linestring);
+      shumate_vector_symbol_info_set_line_points (symbol_info, &linestring);
+    }
+
   g_ptr_array_add (scope->symbols, symbol_info);
 }
 
diff --git a/shumate/vector/shumate-vector-symbol.c b/shumate/vector/shumate-vector-symbol.c
index ed65242..e24a750 100644
--- a/shumate/vector/shumate-vector-symbol.c
+++ b/shumate/vector/shumate-vector-symbol.c
@@ -19,6 +19,8 @@
 #include "shumate-vector-symbol-private.h"
 #include "shumate-vector-utils-private.h"
 #include "shumate-vector-symbol-info-private.h"
+#include "shumate-vector-symbol-container-private.h"
+#include "shumate-layer.h"
 
 
 struct _ShumateVectorSymbol
@@ -26,6 +28,10 @@ struct _ShumateVectorSymbol
   GtkWidget parent_instance;
 
   ShumateVectorSymbolInfo *symbol_info;
+
+  GArray *glyphs;
+  int glyphs_length;
+  double line_length;
 };
 
 G_DEFINE_TYPE (ShumateVectorSymbol, shumate_vector_symbol, GTK_TYPE_WIDGET)
@@ -40,6 +46,18 @@ enum {
 static GParamSpec *obj_properties[N_PROPS] = { NULL, };
 
 
+typedef struct {
+  GskRenderNode *node;
+  double width;
+} Glyph;
+
+static void
+glyph_clear (Glyph *glyph)
+{
+  g_clear_pointer (&glyph->node, gsk_render_node_unref);
+}
+
+
 ShumateVectorSymbol *
 shumate_vector_symbol_new (ShumateVectorSymbolInfo *symbol_info)
 {
@@ -74,9 +92,60 @@ shumate_vector_symbol_constructed (GObject *object)
   attr = pango_attr_size_new_absolute (self->symbol_info->text_size * PANGO_SCALE);
   pango_attr_list_insert (attrs, attr);
 
-  GtkWidget *label = gtk_label_new (self->symbol_info->text);
-  gtk_label_set_attributes (GTK_LABEL (label), attrs);
-  gtk_widget_set_parent (label, GTK_WIDGET (self));
+  if (self->symbol_info->line_placement)
+    {
+      PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (self));
+      g_autoptr(PangoLayout) layout = pango_layout_new (context);
+      g_autoptr(PangoLayoutIter) iter = NULL;
+      PangoGlyphItem *current_item;
+      PangoGlyphString *glyph_string;
+      int i;
+
+      self->glyphs = g_array_new (FALSE, FALSE, sizeof (Glyph));
+      g_array_set_clear_func (self->glyphs, (GDestroyNotify)glyph_clear);
+
+      pango_layout_set_attributes (layout, attrs);
+      pango_layout_set_text (layout, self->symbol_info->text, -1);
+      iter = pango_layout_get_iter (layout);
+
+      pango_layout_get_size (layout, &self->glyphs_length, NULL);
+      self->glyphs_length /= PANGO_SCALE;
+      self->line_length = shumate_vector_line_string_length (&self->symbol_info->line);
+
+      do {
+        current_item = pango_layout_iter_get_run (iter);
+
+        if (current_item == NULL)
+          continue;
+
+        for (i = 0; i < current_item->glyphs->num_glyphs; i ++)
+          {
+            GskRenderNode *node;
+            Glyph glyph;
+
+            glyph_string = pango_glyph_string_new ();
+            pango_glyph_string_set_size (glyph_string, 1);
+            glyph_string->glyphs[0] = current_item->glyphs->glyphs[i];
+            glyph_string->log_clusters[0] = 0;
+
+            node =
+              gsk_text_node_new (current_item->item->analysis.font,
+                                 glyph_string,
+                                 &self->symbol_info->text_color,
+                                 &GRAPHENE_POINT_INIT (0, 0));
+
+            glyph.node = node;
+            glyph.width = glyph_string->glyphs[0].geometry.width / (double) PANGO_SCALE;
+            g_array_append_vals (self->glyphs, &glyph, 1);
+          }
+      } while (pango_layout_iter_next_run (iter));
+    }
+  else
+    {
+      GtkWidget *label = gtk_label_new (self->symbol_info->text);
+      gtk_label_set_attributes (GTK_LABEL (label), attrs);
+      gtk_widget_set_parent (label, GTK_WIDGET (self));
+    }
 
   G_OBJECT_CLASS (shumate_vector_symbol_parent_class)->constructed (object);
 }
@@ -92,6 +161,7 @@ shumate_vector_symbol_dispose (GObject *object)
     gtk_widget_unparent (child);
 
   g_clear_pointer (&self->symbol_info, shumate_vector_symbol_info_unref);
+  g_clear_pointer (&self->glyphs, g_array_unref);
 
   G_OBJECT_CLASS (shumate_vector_symbol_parent_class)->dispose (object);
 }
@@ -138,6 +208,67 @@ shumate_vector_symbol_set_property (GObject      *object,
 }
 
 
+static void
+shumate_vector_symbol_snapshot (GtkWidget   *widget,
+                                GtkSnapshot *snapshot)
+{
+  ShumateVectorSymbol *self = SHUMATE_VECTOR_SYMBOL (widget);
+
+  if (self->symbol_info->line_placement)
+    {
+      GtkWidget *parent = gtk_widget_get_parent (widget);
+      int i;
+      ShumateVectorPointIter iter;
+      double rotation = 0.0;
+      double scale = 512.0;
+
+      if (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (parent))
+        {
+          ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER (parent));
+          ShumateMapSource *map_source = shumate_vector_symbol_container_get_map_source 
(SHUMATE_VECTOR_SYMBOL_CONTAINER (parent));
+          scale = shumate_map_source_get_tile_size_at_zoom (map_source, shumate_viewport_get_zoom_level 
(viewport));
+          rotation = shumate_viewport_get_rotation (viewport);
+        }
+
+      if (self->glyphs_length > self->line_length * scale)
+        return;
+
+      gtk_snapshot_rotate (snapshot, rotation * 180 / G_PI);
+
+      shumate_vector_point_iter_init (&iter, &self->symbol_info->line);
+      shumate_vector_point_iter_advance (&iter, (self->line_length - self->glyphs_length / scale) / 2.0);
+
+      for (i = 0; i < self->glyphs->len; i ++)
+        {
+          Glyph *glyph = &((Glyph *)self->glyphs->data)[i];
+          ShumateVectorPoint point;
+
+          shumate_vector_point_iter_get_current_point (&iter, &point);
+
+          /* Whitespace has no glyph, but still has a width that needs to be
+           * advanced in the point iter */
+          if (glyph->node != NULL)
+            {
+              gtk_snapshot_save (snapshot);
+              gtk_snapshot_translate (snapshot,
+                                      &GRAPHENE_POINT_INIT (
+                                        (point.x - self->symbol_info->x) * scale,
+                                        (point.y - self->symbol_info->y) * scale
+                                      ));
+              gtk_snapshot_rotate (snapshot, shumate_vector_point_iter_get_current_angle (&iter) * 180 / 
G_PI);
+              gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, self->symbol_info->text_size / 2));
+              gtk_snapshot_append_node (snapshot, glyph->node);
+              gtk_snapshot_restore (snapshot);
+            }
+
+          shumate_vector_point_iter_advance (&iter, glyph->width / scale);
+        }
+    }
+  else
+    GTK_WIDGET_CLASS (shumate_vector_symbol_parent_class)->snapshot (widget, snapshot);
+}
+
+
 static void
 shumate_vector_symbol_class_init (ShumateVectorSymbolClass *klass)
 {
@@ -149,6 +280,8 @@ shumate_vector_symbol_class_init (ShumateVectorSymbolClass *klass)
   object_class->get_property = shumate_vector_symbol_get_property;
   object_class->set_property = shumate_vector_symbol_set_property;
 
+  widget_class->snapshot = shumate_vector_symbol_snapshot;
+
   obj_properties[PROP_SYMBOL_INFO] =
     g_param_spec_boxed ("symbol-info",
                         "Symbol info",
@@ -156,9 +289,8 @@ shumate_vector_symbol_class_init (ShumateVectorSymbolClass *klass)
                         SHUMATE_TYPE_VECTOR_SYMBOL_INFO,
                         G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
 
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
   g_object_class_install_properties (object_class, N_PROPS, obj_properties);
-
-  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
 }
 
 
diff --git a/shumate/vector/shumate-vector-utils-private.h b/shumate/vector/shumate-vector-utils-private.h
index 51cf3ea..3c0d511 100644
--- a/shumate/vector/shumate-vector-utils-private.h
+++ b/shumate/vector/shumate-vector-utils-private.h
@@ -19,14 +19,26 @@
 
 #include <json-glib/json-glib.h>
 
+typedef struct _ShumateVectorPoint ShumateVectorPoint;
+typedef struct _ShumateVectorLineString ShumateVectorLineString;
+typedef struct _ShumateVectorPointIter ShumateVectorPointIter;
 
 struct _ShumateVectorPoint {
   float x;
   float y;
 };
 
-typedef struct _ShumateVectorPoint ShumateVectorPoint;
+struct _ShumateVectorLineString {
+  gsize n_points;
+  ShumateVectorPoint *points;
+};
 
+struct _ShumateVectorPointIter {
+  gsize num_points;
+  ShumateVectorPoint *points;
+  gsize current_point;
+  float distance;
+};
 
 gboolean shumate_vector_json_get_object (JsonNode    *node,
                                          JsonObject **dest,
@@ -60,3 +72,25 @@ gboolean shumate_vector_json_read_int_member (JsonReader *reader,
 gboolean shumate_vector_json_read_double_member (JsonReader *reader,
                                                  const char *name,
                                                  double     *dest);
+
+gboolean shumate_vector_json_get_object (JsonNode *node, JsonObject **dest, GError **error);
+gboolean shumate_vector_json_get_array (JsonNode *node, JsonArray **dest, GError **error);
+
+void   shumate_vector_point_iter_init                 (ShumateVectorPointIter  *iter,
+                                                       ShumateVectorLineString *linestring);
+double shumate_vector_point_iter_next_segment         (ShumateVectorPointIter *iter);
+double shumate_vector_point_iter_get_segment_length   (ShumateVectorPointIter *iter);
+void   shumate_vector_point_iter_get_segment_center   (ShumateVectorPointIter *iter,
+                                                       double                  remaining_distance,
+                                                       ShumateVectorPoint     *result);
+void   shumate_vector_point_iter_get_current_point    (ShumateVectorPointIter *iter,
+                                                       ShumateVectorPoint     *result);
+double shumate_vector_point_iter_get_average_angle    (ShumateVectorPointIter *iter,
+                                                       double                  remaining_distance);
+void   shumate_vector_point_iter_advance              (ShumateVectorPointIter *iter,
+                                                       double                  distance);
+double shumate_vector_point_iter_get_current_angle    (ShumateVectorPointIter *iter);
+
+
+void shumate_vector_line_string_clear                 (ShumateVectorLineString *linestring);
+double shumate_vector_line_string_length              (ShumateVectorLineString *linestring);
diff --git a/shumate/vector/shumate-vector-utils.c b/shumate/vector/shumate-vector-utils.c
index c1bfac2..06dcf73 100644
--- a/shumate/vector/shumate-vector-utils.c
+++ b/shumate/vector/shumate-vector-utils.c
@@ -153,3 +153,163 @@ shumate_vector_json_get_string_member (JsonObject  *object,
 
   return shumate_vector_json_get_string (node, dest, error);
 }
+
+
+void
+shumate_vector_point_iter_init (ShumateVectorPointIter  *iter,
+                                ShumateVectorLineString *linestring)
+{
+  *iter = (ShumateVectorPointIter) {
+    .num_points = linestring->n_points,
+    .points = linestring->points,
+    .current_point = 0,
+    .distance = 0,
+  };
+}
+
+
+double
+shumate_vector_point_iter_next_segment (ShumateVectorPointIter *iter)
+{
+  double res = shumate_vector_point_iter_get_segment_length (iter) - iter->distance;
+  iter->distance = 0;
+  iter->current_point ++;
+  return res;
+}
+
+
+static double
+point_distance (ShumateVectorPoint *a, ShumateVectorPoint *b)
+{
+  double x = a->x - b->x;
+  double y = a->y - b->y;
+  return sqrt (x*x + y*y);
+}
+
+static ShumateVectorPoint *
+get_prev_point (ShumateVectorPointIter *iter)
+{
+  return &iter->points[iter->current_point];
+}
+
+static ShumateVectorPoint *
+get_next_point (ShumateVectorPointIter *iter)
+{
+  if (iter->current_point >= iter->num_points - 1)
+    return &iter->points[iter->num_points - 1];
+  else
+    return &iter->points[iter->current_point + 1];
+}
+
+static void
+normalize (ShumateVectorPoint *point)
+{
+  double len = sqrt (point->x*point->x + point->y*point->y);
+  if (len == 0)
+    {
+      point->x = 0;
+      point->y = 0;
+    }
+  else
+    {
+      point->x /= len;
+      point->y /= len;
+    }
+}
+
+
+double
+shumate_vector_point_iter_get_segment_length (ShumateVectorPointIter *iter)
+{
+  ShumateVectorPoint *prev = get_prev_point (iter), *next = get_next_point (iter);
+  return point_distance (prev, next);
+}
+
+
+void
+shumate_vector_point_iter_get_current_point (ShumateVectorPointIter *iter,
+                                             ShumateVectorPoint     *result)
+{
+  ShumateVectorPoint *prev = get_prev_point (iter), *next = get_next_point (iter);
+
+  result->x = next->x - prev->x;
+  result->y = next->y - prev->y;
+  normalize (result);
+
+  result->x *= iter->distance;
+  result->y *= iter->distance;
+  result->x += prev->x;
+  result->y += prev->y;
+}
+
+
+void
+shumate_vector_point_iter_advance (ShumateVectorPointIter *iter,
+                                   double                  distance)
+{
+  while (distance > 0)
+    {
+      if (iter->current_point >= iter->num_points - 1)
+        return;
+
+      if (iter->distance + distance > shumate_vector_point_iter_get_segment_length (iter))
+        distance -= shumate_vector_point_iter_next_segment (iter);
+      else
+        {
+          iter->distance += distance;
+          return;
+        }
+    }
+}
+
+
+double
+shumate_vector_point_iter_get_current_angle (ShumateVectorPointIter *iter)
+{
+  ShumateVectorPoint *prev = get_prev_point (iter), *next = get_next_point (iter);
+  return atan2 (next->y - prev->y, next->x - prev->x);
+}
+
+
+double
+shumate_vector_point_iter_get_average_angle (ShumateVectorPointIter *iter,
+                                             double                  remaining_distance)
+{
+  ShumateVectorPointIter iter2 = *iter;
+  double orig = shumate_vector_point_iter_get_current_angle (iter);
+  double orig_distance = remaining_distance;
+  double sum = 0.0;
+
+  while (remaining_distance > 0)
+    {
+      float angle = shumate_vector_point_iter_get_current_angle (&iter2);
+      float d = MIN (remaining_distance, shumate_vector_point_iter_next_segment (&iter2));
+      sum += d * (angle - orig);
+      remaining_distance -= d;
+    }
+
+  return sum / orig_distance;
+}
+
+
+void
+shumate_vector_line_string_clear (ShumateVectorLineString *linestring)
+{
+  g_clear_pointer (&linestring->points, g_free);
+  linestring->n_points = 0;
+}
+
+
+double
+shumate_vector_line_string_length (ShumateVectorLineString *linestring)
+{
+  ShumateVectorPointIter iter;
+  double len, sum = 0;
+
+  shumate_vector_point_iter_init (&iter, linestring);
+
+  while ((len = shumate_vector_point_iter_next_segment (&iter)))
+    sum += len;
+
+  return sum;
+}


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