LCOV - code coverage report
Current view: top level - shumate/vector - shumate-vector-symbol.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 0 508 0.0 %
Date: 2024-05-11 21:41:31 Functions: 0 23 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 218 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2021 James Westman <james@jwestman.net>
       3                 :            :  *
       4                 :            :  * This library is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Lesser General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2.1 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * This library is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12                 :            :  * Lesser General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU Lesser General Public
      15                 :            :  * License along with this library; if not, see <https://www.gnu.org/licenses/>.
      16                 :            :  */
      17                 :            : 
      18                 :            : 
      19                 :            : #include "shumate-vector-symbol-private.h"
      20                 :            : #include "shumate-vector-utils-private.h"
      21                 :            : #include "shumate-vector-symbol-info-private.h"
      22                 :            : #include "shumate-vector-symbol-container-private.h"
      23                 :            : #include "shumate-layer.h"
      24                 :            : #include "shumate-symbol-event-private.h"
      25                 :            : #include "../shumate-vector-sprite.h"
      26                 :            : 
      27                 :            : #define RGBA_BLACK ((GdkRGBA){0, 0, 0, 1})
      28                 :            : 
      29                 :            : struct _ShumateVectorSymbol
      30                 :            : {
      31                 :            :   GtkWidget parent_instance;
      32                 :            : 
      33                 :            :   ShumateVectorSymbolInfo *symbol_info;
      34                 :            : 
      35                 :            :   GArray *glyphs;
      36                 :            : 
      37                 :            :   GskRenderNode *glyphs_node;
      38                 :            :   int layout_width, layout_height, baseline, layout_y;
      39                 :            : 
      40                 :            :   graphene_rect_t bounds;
      41                 :            :   double x, y;
      42                 :            : 
      43                 :            :   ShumateVectorPoint midpoint;
      44                 :            :   double midpoint_angle;
      45                 :            :   double line_length;
      46                 :            : 
      47                 :            :   uint8_t show_text : 1;
      48                 :            :   uint8_t show_icon : 1;
      49                 :            : };
      50                 :            : 
      51   [ #  #  #  # ]:          0 : G_DEFINE_TYPE (ShumateVectorSymbol, shumate_vector_symbol, GTK_TYPE_WIDGET)
      52                 :            : 
      53                 :            : 
      54                 :            : enum {
      55                 :            :   PROP_0,
      56                 :            :   PROP_SYMBOL_INFO,
      57                 :            :   N_PROPS,
      58                 :            : };
      59                 :            : 
      60                 :            : static GParamSpec *obj_properties[N_PROPS] = { NULL, };
      61                 :            : 
      62                 :            : enum {
      63                 :            :   CLICKED,
      64                 :            :   LAST_SIGNAL
      65                 :            : };
      66                 :            : 
      67                 :            : static guint signals[LAST_SIGNAL] = { 0, };
      68                 :            : 
      69                 :            : 
      70                 :            : typedef struct {
      71                 :            :   GskRenderNode *node;
      72                 :            :   ShumateVectorSprite *sprite;
      73                 :            :   double width;
      74                 :            :   GdkRGBA icon_color;
      75                 :            : } Glyph;
      76                 :            : 
      77                 :            : static void
      78                 :          0 : glyph_clear (Glyph *glyph)
      79                 :            : {
      80         [ #  # ]:          0 :   g_clear_pointer (&glyph->node, gsk_render_node_unref);
      81                 :          0 : }
      82                 :            : 
      83                 :            : 
      84                 :            : ShumateVectorSymbol *
      85                 :          0 : shumate_vector_symbol_new (ShumateVectorSymbolInfo *symbol_info)
      86                 :            : {
      87                 :          0 :   return g_object_new (SHUMATE_TYPE_VECTOR_SYMBOL,
      88                 :            :                        "symbol-info", symbol_info,
      89                 :            :                        NULL);
      90                 :            : }
      91                 :            : 
      92                 :            : 
      93                 :            : static PangoAttrShape *
      94                 :          0 : get_shape_from_glyph_item (PangoGlyphItem *item)
      95                 :            : {
      96         [ #  # ]:          0 :   for (GSList *list = item->item->analysis.extra_attrs; list != NULL; list = list->next)
      97         [ #  # ]:          0 :     if (((PangoAttribute*)list->data)->klass->type == PANGO_ATTR_SHAPE)
      98                 :          0 :       return (PangoAttrShape*)list->data;
      99                 :            :   return NULL;
     100                 :            : }
     101                 :            : 
     102                 :            : static void
     103                 :          0 : get_color_from_glyph_item (PangoGlyphItem *item, GdkRGBA *color)
     104                 :            : {
     105                 :          0 :   *color = RGBA_BLACK;
     106                 :            : 
     107         [ #  # ]:          0 :   for (GSList *list = item->item->analysis.extra_attrs; list != NULL; list = list->next)
     108                 :            :     {
     109         [ #  # ]:          0 :       if (((PangoAttribute*)list->data)->klass->type == PANGO_ATTR_FOREGROUND)
     110                 :            :         {
     111                 :          0 :           PangoColor c = ((PangoAttrColor*)list->data)->color;
     112                 :          0 :           color->red = c.red / 65535.0;
     113                 :          0 :           color->green = c.green / 65535.0;
     114                 :          0 :           color->blue = c.blue / 65535.0;
     115                 :            :         }
     116         [ #  # ]:          0 :       else if (((PangoAttribute*)list->data)->klass->type == PANGO_ATTR_FOREGROUND_ALPHA)
     117                 :          0 :         color->alpha = ((PangoAttrInt*)list->data)->value / 65535.0;
     118                 :            :     }
     119                 :          0 : }
     120                 :            : 
     121                 :            : 
     122                 :            : static void
     123                 :          0 : shumate_vector_symbol_constructed (GObject *object)
     124                 :            : {
     125                 :          0 :   ShumateVectorSymbol *self = (ShumateVectorSymbol *)object;
     126                 :          0 :   g_autofree char *text = NULL;
     127                 :            : 
     128         [ #  # ]:          0 :   if (self->symbol_info->details->formatted_text != NULL)
     129                 :            :     {
     130                 :          0 :       PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (self));
     131                 :          0 :       g_autoptr(PangoLayout) layout = pango_layout_new (context);
     132         [ #  # ]:          0 :       g_autoptr(PangoAttrList) attrs = pango_attr_list_new ();
     133         [ #  # ]:          0 :       g_autoptr(GString) string = g_string_new ("");
     134                 :          0 :       PangoAttribute *attr;
     135                 :          0 :       gboolean any_images = FALSE;
     136                 :          0 :       PangoRectangle ink_rect;
     137                 :            : 
     138         [ #  # ]:          0 :       if (self->symbol_info->details->text_font != NULL)
     139                 :            :         {
     140                 :          0 :           g_autoptr(PangoFontDescription) desc = pango_font_description_from_string (self->symbol_info->details->text_font);
     141                 :          0 :           attr = pango_attr_font_desc_new (desc);
     142         [ #  # ]:          0 :           pango_attr_list_insert (attrs, attr);
     143                 :            :         }
     144                 :            : 
     145                 :          0 :       attr = pango_attr_letter_spacing_new (self->symbol_info->details->text_letter_spacing * self->symbol_info->details->text_size * PANGO_SCALE);
     146                 :          0 :       pango_attr_list_insert (attrs, attr);
     147                 :            : 
     148                 :          0 :       attr = pango_attr_foreground_new (self->symbol_info->details->text_color.red * 65535,
     149                 :          0 :                                         self->symbol_info->details->text_color.green * 65535,
     150                 :          0 :                                         self->symbol_info->details->text_color.blue * 65535);
     151                 :          0 :       pango_attr_list_insert (attrs, attr);
     152                 :            : 
     153                 :          0 :       attr = pango_attr_foreground_alpha_new (self->symbol_info->details->text_color.alpha * 65535);
     154                 :          0 :       pango_attr_list_insert (attrs, attr);
     155                 :            : 
     156                 :          0 :       attr = pango_attr_size_new_absolute (self->symbol_info->details->text_size * PANGO_SCALE);
     157                 :          0 :       pango_attr_list_insert (attrs, attr);
     158                 :            : 
     159                 :          0 :       pango_layout_set_attributes (layout, attrs);
     160                 :            : 
     161         [ #  # ]:          0 :       for (int i = 0, n = self->symbol_info->details->formatted_text->len; i < n; i ++)
     162                 :            :         {
     163                 :          0 :           ShumateVectorFormatPart *part = g_ptr_array_index (self->symbol_info->details->formatted_text, i);
     164                 :            : 
     165         [ #  # ]:          0 :           if (part->sprite != NULL)
     166                 :            :             {
     167                 :          0 :               int width = shumate_vector_sprite_get_width (part->sprite);
     168                 :          0 :               int height = shumate_vector_sprite_get_height (part->sprite);
     169                 :            :               /* For shapes, since we're overriding the ink and logical rects
     170                 :            :                  of the glyph, we have to take letter spacing into account ourselves */
     171                 :          0 :               double spacing = self->symbol_info->details->text_letter_spacing * self->symbol_info->details->text_size;
     172                 :            : 
     173                 :          0 :               PangoRectangle ink_rect = {
     174                 :          0 :                 .x = spacing / 2 * PANGO_SCALE,
     175                 :          0 :                 .y = -height * PANGO_SCALE,
     176                 :          0 :                 .width = width * PANGO_SCALE,
     177                 :            :                 .height = height * PANGO_SCALE,
     178                 :            :               };
     179                 :          0 :               PangoRectangle logical_rect = {
     180                 :            :                 .x = 0,
     181                 :            :                 .y = -height * PANGO_SCALE,
     182                 :          0 :                 .width = (width + spacing) * PANGO_SCALE,
     183                 :            :                 .height = height * PANGO_SCALE,
     184                 :            :               };
     185                 :            : 
     186                 :          0 :               attr = pango_attr_shape_new_with_data (&ink_rect, &logical_rect, g_object_ref (part->sprite), (PangoAttrDataCopyFunc)g_object_ref, g_object_unref);
     187                 :          0 :               attr->start_index = string->len;
     188                 :          0 :               attr->end_index = string->len + strlen ("\uFFFC");
     189                 :          0 :               pango_attr_list_insert (attrs, attr);
     190                 :            : 
     191         [ #  # ]:          0 :               if (spacing != 0)
     192                 :            :                 {
     193                 :          0 :                   attr = pango_attr_letter_spacing_new (0);
     194                 :          0 :                   attr->start_index = string->len;
     195                 :          0 :                   attr->end_index = string->len + strlen ("\uFFFC");
     196                 :          0 :                   pango_attr_list_insert (attrs, attr);
     197                 :            :                 }
     198                 :            : 
     199         [ #  # ]:          0 :               g_string_append (string, "\uFFFC");
     200                 :            : 
     201                 :          0 :               any_images = TRUE;
     202                 :            :             }
     203                 :            :           else
     204                 :            :             {
     205         [ #  # ]:          0 :               if (part->has_font_scale)
     206                 :            :                 {
     207                 :          0 :                   attr = pango_attr_size_new_absolute (part->font_scale * self->symbol_info->details->text_size * PANGO_SCALE);
     208                 :          0 :                   attr->start_index = string->len;
     209                 :          0 :                   attr->end_index = string->len + strlen (part->string);
     210                 :          0 :                   pango_attr_list_insert (attrs, attr);
     211                 :            :                 }
     212                 :            : 
     213         [ #  # ]:          0 :               if (part->has_text_color)
     214                 :            :                 {
     215                 :          0 :                   attr = pango_attr_foreground_new (part->text_color.red * 65535,
     216                 :          0 :                                                     part->text_color.green * 65535,
     217                 :          0 :                                                     part->text_color.blue * 65535);
     218                 :          0 :                   attr->start_index = string->len;
     219                 :          0 :                   attr->end_index = string->len + strlen (part->string);
     220                 :          0 :                   pango_attr_list_insert (attrs, attr);
     221                 :            : 
     222                 :          0 :                   attr = pango_attr_foreground_alpha_new (part->text_color.alpha * 65535);
     223                 :          0 :                   attr->start_index = string->len;
     224                 :          0 :                   attr->end_index = string->len + strlen (part->string);
     225                 :          0 :                   pango_attr_list_insert (attrs, attr);
     226                 :            :                 }
     227                 :            : 
     228         [ #  # ]:          0 :               g_string_append (string, part->string);
     229                 :            :             }
     230                 :            :         }
     231                 :            : 
     232                 :          0 :       text = g_string_free (g_steal_pointer (&string), FALSE);
     233                 :          0 :       pango_layout_set_text (layout, text, -1);
     234                 :            : 
     235                 :          0 :       pango_layout_get_pixel_extents (layout, &ink_rect, NULL);
     236                 :          0 :       self->layout_width = ink_rect.width;
     237                 :          0 :       self->layout_height = ink_rect.height;
     238                 :          0 :       self->layout_y = ink_rect.y;
     239                 :          0 :       self->baseline = pango_layout_get_baseline (layout) / (double) PANGO_SCALE;
     240                 :            : 
     241                 :          0 :       if ((self->symbol_info->details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_MAP
     242         [ #  # ]:          0 :           || self->symbol_info->details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_VIEWPORT_GLYPH)
     243                 :          0 :           && (self->symbol_info->details->symbol_placement == SHUMATE_VECTOR_PLACEMENT_LINE
     244         [ #  # ]:          0 :               || self->symbol_info->details->symbol_placement == SHUMATE_VECTOR_PLACEMENT_LINE_CENTER))
     245                 :            :         {
     246                 :          0 :           g_autoptr(PangoLayoutIter) iter = NULL;
     247                 :          0 :           int i;
     248                 :          0 :           PangoGlyphItem *current_item;
     249                 :            : 
     250                 :          0 :           self->glyphs = g_array_new (FALSE, FALSE, sizeof (Glyph));
     251                 :          0 :           g_array_set_clear_func (self->glyphs, (GDestroyNotify)glyph_clear);
     252                 :            : 
     253                 :          0 :           iter = pango_layout_get_iter (layout);
     254                 :            : 
     255                 :          0 :           do {
     256                 :          0 :             PangoAttrShape *shape = NULL;
     257                 :            : 
     258                 :          0 :             current_item = pango_layout_iter_get_run (iter);
     259                 :            : 
     260         [ #  # ]:          0 :             if (current_item == NULL)
     261                 :          0 :               continue;
     262                 :            : 
     263                 :          0 :             shape = get_shape_from_glyph_item (current_item);
     264                 :            : 
     265         [ #  # ]:          0 :             if (shape != NULL)
     266                 :            :               {
     267                 :          0 :                 Glyph glyph = {
     268                 :          0 :                   .sprite = g_object_ref (shape->data),
     269                 :            :                   .node = NULL,
     270                 :          0 :                   .width = shape->logical_rect.width / (double) PANGO_SCALE,
     271                 :            :                 };
     272                 :          0 :                 get_color_from_glyph_item (current_item, &glyph.icon_color);
     273                 :          0 :                 g_array_append_vals (self->glyphs, &glyph, 1);
     274                 :            :               }
     275                 :            :             else
     276                 :            :               {
     277         [ #  # ]:          0 :                 for (i = 0; i < current_item->glyphs->num_glyphs; i ++)
     278                 :            :                   {
     279                 :          0 :                     GskRenderNode *node;
     280                 :          0 :                     Glyph glyph;
     281                 :          0 :                     PangoGlyphString *glyph_string;
     282                 :          0 :                     GdkRGBA color;
     283                 :            : 
     284                 :          0 :                     glyph_string = pango_glyph_string_new ();
     285                 :          0 :                     pango_glyph_string_set_size (glyph_string, 1);
     286                 :          0 :                     glyph_string->glyphs[0] = current_item->glyphs->glyphs[i];
     287                 :          0 :                     glyph_string->log_clusters[0] = 0;
     288                 :            : 
     289                 :          0 :                     get_color_from_glyph_item (current_item, &color);
     290                 :            : 
     291                 :          0 :                     node =
     292                 :          0 :                       gsk_text_node_new (current_item->item->analysis.font,
     293                 :            :                                          glyph_string,
     294                 :            :                                          &color,
     295                 :          0 :                                          &GRAPHENE_POINT_INIT (0, 0));
     296                 :            : 
     297                 :          0 :                     glyph.node = node;
     298                 :          0 :                     glyph.sprite = NULL;
     299                 :          0 :                     glyph.width = glyph_string->glyphs[0].geometry.width / (double) PANGO_SCALE;
     300                 :          0 :                     g_array_append_vals (self->glyphs, &glyph, 1);
     301                 :            : 
     302                 :          0 :                     pango_glyph_string_free (glyph_string);
     303                 :            :                   }
     304                 :            :               }
     305         [ #  # ]:          0 :           } while (pango_layout_iter_next_run (iter));
     306                 :            :         }
     307                 :            :       else
     308                 :            :         {
     309                 :          0 :           g_autoptr(GtkSnapshot) snapshot = gtk_snapshot_new ();
     310                 :            : 
     311                 :          0 :           gtk_snapshot_append_layout (snapshot, layout, &RGBA_BLACK);
     312                 :            : 
     313         [ #  # ]:          0 :           if (any_images)
     314                 :            :             {
     315                 :          0 :               g_autoptr(PangoLayoutIter) iter = NULL;
     316                 :            : 
     317                 :          0 :               iter = pango_layout_get_iter (layout);
     318                 :            : 
     319                 :          0 :               do {
     320                 :          0 :                 PangoGlyphItem *current_item = pango_layout_iter_get_run (iter);
     321                 :          0 :                 PangoAttrShape *shape;
     322                 :            : 
     323         [ #  # ]:          0 :                 if (current_item == NULL)
     324                 :          0 :                   continue;
     325                 :            : 
     326                 :          0 :                 shape = get_shape_from_glyph_item (current_item);
     327         [ #  # ]:          0 :                 if (shape != NULL)
     328                 :            :                   {
     329                 :          0 :                     ShumateVectorSprite *sprite = shape->data;
     330                 :          0 :                     PangoRectangle extents;
     331                 :          0 :                     GdkRGBA color;
     332                 :            : 
     333                 :          0 :                     pango_layout_iter_get_run_extents (iter, &extents, NULL);
     334                 :          0 :                     get_color_from_glyph_item (current_item, &color);
     335                 :            : 
     336                 :          0 :                     gtk_snapshot_save (snapshot);
     337                 :          0 :                     gtk_snapshot_translate (snapshot,
     338                 :          0 :                                             &GRAPHENE_POINT_INIT (
     339                 :            :                                               PANGO_PIXELS (extents.x),
     340                 :            :                                               PANGO_PIXELS (extents.y)
     341                 :            :                                             ));
     342                 :          0 :                     gtk_symbolic_paintable_snapshot_symbolic (
     343                 :            :                       GTK_SYMBOLIC_PAINTABLE (sprite),
     344                 :            :                       snapshot,
     345                 :          0 :                       PANGO_PIXELS (extents.width),
     346                 :          0 :                       PANGO_PIXELS (extents.height),
     347                 :            :                       &color,
     348                 :            :                       1
     349                 :            :                     );
     350                 :          0 :                     gtk_snapshot_restore (snapshot);
     351                 :            :                   }
     352         [ #  # ]:          0 :               } while (pango_layout_iter_next_run (iter));
     353                 :            :             }
     354                 :            : 
     355                 :          0 :           self->glyphs_node = gtk_snapshot_free_to_node (g_steal_pointer (&snapshot));
     356                 :            :         }
     357                 :            :     }
     358                 :            : 
     359         [ #  # ]:          0 :   if (self->symbol_info->details->cursor != NULL)
     360                 :          0 :     gtk_widget_set_cursor_from_name (GTK_WIDGET (self), self->symbol_info->details->cursor);
     361                 :            : 
     362         [ #  # ]:          0 :   if (self->symbol_info->line != NULL)
     363                 :            :     {
     364                 :          0 :       ShumateVectorPointIter iter;
     365                 :            : 
     366                 :          0 :       shumate_vector_point_iter_init (&iter, self->symbol_info->line);
     367                 :          0 :       shumate_vector_point_iter_advance (&iter, self->symbol_info->line_position);
     368                 :            : 
     369                 :          0 :       shumate_vector_point_iter_get_current_point (&iter, &self->midpoint);
     370                 :          0 :       self->midpoint.x -= self->symbol_info->x;
     371                 :          0 :       self->midpoint.y -= self->symbol_info->y;
     372                 :            : 
     373                 :          0 :       self->midpoint_angle = shumate_vector_point_iter_get_current_angle (&iter);
     374                 :            : 
     375                 :          0 :       self->line_length = shumate_vector_line_string_length (self->symbol_info->line);
     376                 :            :     }
     377                 :            : 
     378                 :          0 :   gtk_accessible_update_property (GTK_ACCESSIBLE (self),
     379                 :            :                                   GTK_ACCESSIBLE_PROPERTY_LABEL,
     380                 :            :                                   text,
     381                 :            :                                   -1);
     382                 :            : 
     383                 :          0 :   G_OBJECT_CLASS (shumate_vector_symbol_parent_class)->constructed (object);
     384                 :          0 : }
     385                 :            : 
     386                 :            : 
     387                 :            : static gboolean
     388                 :          0 : shumate_vector_symbol_contains (GtkWidget *widget,
     389                 :            :                                 double     x,
     390                 :            :                                 double     y)
     391                 :            : {
     392                 :          0 :   GtkWidget *parent;
     393                 :          0 :   graphene_rect_t alloc;
     394                 :            : 
     395   [ #  #  #  # ]:          0 :   if (x < 0 || y < 0)
     396                 :            :     return FALSE;
     397                 :            : 
     398                 :          0 :   parent = gtk_widget_get_parent (widget);
     399         [ #  # ]:          0 :   if (!gtk_widget_compute_bounds (widget, parent, &alloc))
     400                 :            :     return FALSE;
     401                 :            : 
     402   [ #  #  #  # ]:          0 :   if (x > alloc.size.width || y > alloc.size.height)
     403                 :            :     return FALSE;
     404                 :            : 
     405         [ #  # ]:          0 :   if (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (parent))
     406                 :            :     {
     407                 :          0 :       ShumateVectorCollision *collision =
     408                 :          0 :         shumate_vector_symbol_container_get_collision ((ShumateVectorSymbolContainer *) (parent));
     409                 :            : 
     410                 :          0 :       return shumate_vector_collision_query_point (collision,
     411                 :          0 :                                                    alloc.origin.x + x + collision->delta_x,
     412                 :          0 :                                                    alloc.origin.y + y + collision->delta_y,
     413                 :            :                                                    widget);
     414                 :            :     }
     415                 :            : 
     416                 :            :   return TRUE;
     417                 :            : }
     418                 :            : 
     419                 :            : 
     420                 :            : static void
     421                 :          0 : shumate_vector_symbol_dispose (GObject *object)
     422                 :            : {
     423                 :          0 :   ShumateVectorSymbol *self = (ShumateVectorSymbol *)object;
     424                 :          0 :   GtkWidget *child;
     425                 :            : 
     426         [ #  # ]:          0 :   while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
     427                 :          0 :     gtk_widget_unparent (child);
     428                 :            : 
     429         [ #  # ]:          0 :   g_clear_pointer (&self->symbol_info, shumate_vector_symbol_info_unref);
     430         [ #  # ]:          0 :   g_clear_pointer (&self->glyphs, g_array_unref);
     431         [ #  # ]:          0 :   g_clear_pointer (&self->glyphs_node, gsk_render_node_unref);
     432                 :            : 
     433                 :          0 :   G_OBJECT_CLASS (shumate_vector_symbol_parent_class)->dispose (object);
     434                 :          0 : }
     435                 :            : 
     436                 :            : 
     437                 :            : static void
     438                 :          0 : shumate_vector_symbol_get_property (GObject    *object,
     439                 :            :                                     guint       property_id,
     440                 :            :                                     GValue     *value,
     441                 :            :                                     GParamSpec *pspec)
     442                 :            : {
     443                 :          0 :   ShumateVectorSymbol *self = (ShumateVectorSymbol *)object;
     444                 :            : 
     445         [ #  # ]:          0 :   switch (property_id)
     446                 :            :     {
     447                 :          0 :     case PROP_SYMBOL_INFO:
     448                 :          0 :       g_value_set_boxed (value, self->symbol_info);
     449                 :          0 :       break;
     450                 :            : 
     451                 :          0 :     default:
     452                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     453                 :            :     }
     454                 :          0 : }
     455                 :            : 
     456                 :            : 
     457                 :            : static void
     458                 :          0 : shumate_vector_symbol_set_property (GObject      *object,
     459                 :            :                                     guint         property_id,
     460                 :            :                                     const GValue *value,
     461                 :            :                                     GParamSpec   *pspec)
     462                 :            : {
     463                 :          0 :   ShumateVectorSymbol *self = SHUMATE_VECTOR_SYMBOL (object);
     464                 :            : 
     465         [ #  # ]:          0 :   switch (property_id)
     466                 :            :     {
     467                 :          0 :     case PROP_SYMBOL_INFO:
     468         [ #  # ]:          0 :       g_assert (self->symbol_info == NULL);
     469                 :          0 :       self->symbol_info = g_value_dup_boxed (value);
     470                 :          0 :       break;
     471                 :            : 
     472                 :          0 :     default:
     473                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     474                 :            :     }
     475                 :          0 : }
     476                 :            : 
     477                 :            : 
     478                 :            : static double
     479                 :          0 : positive_mod (double i, double n)
     480                 :            : {
     481                 :          0 :   return fmod (fmod (i, n) + n, n);
     482                 :            : }
     483                 :            : 
     484                 :            : 
     485                 :            : static void
     486                 :          0 : shumate_vector_symbol_measure (GtkWidget      *widget,
     487                 :            :                                GtkOrientation  orientation,
     488                 :            :                                int             for_size,
     489                 :            :                                int            *minimum,
     490                 :            :                                int            *natural,
     491                 :            :                                int            *minimum_baseline,
     492                 :            :                                int            *natural_baseline)
     493                 :            : {
     494                 :          0 :   ShumateVectorSymbol *self = SHUMATE_VECTOR_SYMBOL (widget);
     495                 :            : 
     496         [ #  # ]:          0 :   if (self->symbol_info->line != NULL)
     497                 :            :     {
     498         [ #  # ]:          0 :       if (minimum)
     499                 :          0 :         *minimum = 0;
     500         [ #  # ]:          0 :       if (natural)
     501                 :          0 :         *natural = 0;
     502                 :            :     }
     503                 :            :   else
     504                 :            :     {
     505         [ #  # ]:          0 :       if (orientation == GTK_ORIENTATION_HORIZONTAL)
     506                 :            :         {
     507         [ #  # ]:          0 :           if (minimum)
     508                 :          0 :             *minimum = self->layout_width;
     509         [ #  # ]:          0 :           if (natural)
     510                 :          0 :             *natural = self->layout_width;
     511                 :            :         }
     512         [ #  # ]:          0 :       else if (orientation == GTK_ORIENTATION_VERTICAL)
     513                 :            :         {
     514         [ #  # ]:          0 :           if (minimum)
     515                 :          0 :             *minimum = self->layout_height;
     516         [ #  # ]:          0 :           if (natural)
     517                 :          0 :             *natural = self->layout_height;
     518                 :            :         }
     519                 :            :     }
     520                 :          0 : }
     521                 :            : 
     522                 :            : static GtkSizeRequestMode
     523                 :          0 : shumate_vector_symbol_get_request_mode (GtkWidget *widget)
     524                 :            : {
     525                 :          0 :   return GTK_SIZE_REQUEST_CONSTANT_SIZE;
     526                 :            : }
     527                 :            : 
     528                 :            : 
     529                 :            : static void
     530                 :          0 : add_anchor_offset (ShumateVectorAnchor anchor,
     531                 :            :                    double *offset_x,
     532                 :            :                    double *offset_y,
     533                 :            :                    double width,
     534                 :            :                    double height)
     535                 :            : {
     536                 :          0 :   if (anchor == SHUMATE_VECTOR_ANCHOR_LEFT
     537         [ #  # ]:          0 :       || anchor == SHUMATE_VECTOR_ANCHOR_TOP_LEFT
     538                 :            :       || anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM_LEFT)
     539                 :          0 :     *offset_x += width / 2.0;
     540                 :            : 
     541                 :          0 :   if (anchor == SHUMATE_VECTOR_ANCHOR_RIGHT
     542         [ #  # ]:          0 :       || anchor == SHUMATE_VECTOR_ANCHOR_TOP_RIGHT
     543                 :            :       || anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM_RIGHT)
     544                 :          0 :     *offset_x -= width / 2.0;
     545                 :            : 
     546                 :          0 :   if (anchor == SHUMATE_VECTOR_ANCHOR_TOP
     547         [ #  # ]:          0 :       || anchor == SHUMATE_VECTOR_ANCHOR_TOP_LEFT
     548         [ #  # ]:          0 :       || anchor == SHUMATE_VECTOR_ANCHOR_TOP_RIGHT)
     549                 :          0 :     *offset_y += height / 2.0;
     550                 :            : 
     551                 :          0 :   if (anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM
     552         [ #  # ]:          0 :       || anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM_LEFT
     553         [ #  # ]:          0 :       || anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM_RIGHT)
     554                 :          0 :     *offset_y -= height / 2.0;
     555                 :          0 : }
     556                 :            : 
     557                 :            : 
     558                 :            : static void
     559                 :          0 : shumate_vector_symbol_snapshot (GtkWidget   *widget,
     560                 :            :                                 GtkSnapshot *snapshot)
     561                 :            : {
     562                 :          0 :   ShumateVectorSymbol *self = SHUMATE_VECTOR_SYMBOL (widget);
     563                 :          0 :   double rotation = 0.0;
     564                 :          0 :   double tile_size_for_zoom = 512.0;
     565                 :          0 :   GtkWidget *parent = gtk_widget_get_parent (widget);
     566                 :          0 :   ShumateVectorPointIter iter;
     567                 :            : 
     568         [ #  # ]:          0 :   if (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (parent))
     569                 :            :     {
     570                 :          0 :       ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER (parent));
     571                 :          0 :       ShumateMapSource *map_source = shumate_vector_symbol_container_get_map_source (SHUMATE_VECTOR_SYMBOL_CONTAINER (parent));
     572                 :          0 :       double zoom_level = shumate_viewport_get_zoom_level (viewport);
     573                 :          0 :       tile_size_for_zoom = shumate_map_source_get_tile_size (map_source) * pow (2, zoom_level - self->symbol_info->details->tile_zoom_level);
     574                 :          0 :       rotation = shumate_viewport_get_rotation (viewport);
     575                 :            :     }
     576                 :            : 
     577                 :          0 :   gtk_snapshot_save (snapshot);
     578                 :            : 
     579                 :            :   /* Translate so the origin is at the origin point of the symbol */
     580                 :          0 :   gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (self->x - self->bounds.origin.x,
     581                 :            :                                                           self->y - self->bounds.origin.y));
     582                 :            : 
     583   [ #  #  #  #  :          0 :   if (self->show_icon && self->symbol_info->details->icon_image && self->symbol_info->details->icon_opacity > 0.0)
                   #  # ]
     584                 :            :     {
     585                 :          0 :       double angle = 0;
     586                 :          0 :       double icon_width = shumate_vector_sprite_get_width (self->symbol_info->details->icon_image)
     587                 :          0 :                           * self->symbol_info->details->icon_size;
     588                 :          0 :       double icon_height = shumate_vector_sprite_get_height (self->symbol_info->details->icon_image)
     589                 :          0 :                            * self->symbol_info->details->icon_size;
     590                 :            : 
     591                 :          0 :       double offset_x = self->symbol_info->details->icon_offset_x * self->symbol_info->details->icon_size;
     592                 :          0 :       double offset_y = self->symbol_info->details->icon_offset_y * self->symbol_info->details->icon_size;
     593                 :            : 
     594                 :          0 :       add_anchor_offset (self->symbol_info->details->icon_anchor,
     595                 :            :                          &offset_x, &offset_y,
     596                 :            :                          icon_width, icon_height);
     597                 :            : 
     598         [ #  # ]:          0 :       if (self->symbol_info->details->icon_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_MAP)
     599                 :          0 :         angle = self->midpoint_angle;
     600                 :            :       else
     601                 :          0 :         angle = -rotation;
     602                 :            : 
     603                 :          0 :       angle += self->symbol_info->details->icon_rotate;
     604                 :            : 
     605                 :          0 :       gtk_snapshot_save (snapshot);
     606                 :            : 
     607                 :          0 :       gtk_snapshot_rotate (snapshot, rotation * 180 / G_PI);
     608                 :          0 :       gtk_snapshot_translate (snapshot,
     609                 :          0 :                               &GRAPHENE_POINT_INIT (
     610                 :            :                                 self->midpoint.x * tile_size_for_zoom,
     611                 :            :                                 self->midpoint.y * tile_size_for_zoom
     612                 :            :                               ));
     613                 :          0 :       gtk_snapshot_rotate (snapshot, angle * 180 / G_PI);
     614                 :          0 :       gtk_snapshot_translate (snapshot,
     615                 :          0 :                               &GRAPHENE_POINT_INIT (
     616                 :            :                                 -icon_width / 2.0 + offset_x,
     617                 :            :                                 -icon_height / 2.0 + offset_y
     618                 :            :                               ));
     619                 :            : 
     620         [ #  # ]:          0 :       if (self->symbol_info->details->icon_opacity < 1.0)
     621                 :          0 :         gtk_snapshot_push_opacity (snapshot, self->symbol_info->details->icon_opacity);
     622                 :            : 
     623                 :          0 :       gtk_symbolic_paintable_snapshot_symbolic (
     624                 :          0 :         GTK_SYMBOLIC_PAINTABLE (self->symbol_info->details->icon_image),
     625                 :            :         snapshot,
     626                 :            :         icon_width,
     627                 :            :         icon_height,
     628                 :          0 :         &self->symbol_info->details->icon_color,
     629                 :            :         1
     630                 :            :       );
     631                 :            : 
     632         [ #  # ]:          0 :       if (self->symbol_info->details->icon_opacity < 1.0)
     633                 :          0 :         gtk_snapshot_pop (snapshot);
     634                 :            : 
     635                 :          0 :       gtk_snapshot_restore (snapshot);
     636                 :            :     }
     637                 :            : 
     638   [ #  #  #  #  :          0 :   if (self->show_text && self->glyphs && self->symbol_info->details->text_opacity > 0.0)
                   #  # ]
     639                 :          0 :     {
     640                 :          0 :       double length = self->layout_width / tile_size_for_zoom;
     641         [ #  # ]:          0 :       double start_pos = MAX (0, self->symbol_info->line_position - (length / 2.0));
     642                 :          0 :       double avg_angle;
     643                 :            : 
     644                 :          0 :       shumate_vector_point_iter_init (&iter, self->symbol_info->line);
     645                 :          0 :       shumate_vector_point_iter_advance (&iter, start_pos);
     646                 :            : 
     647                 :            :       /* If the label is upside down on average, draw it the other way around */
     648         [ #  # ]:          0 :       if (self->symbol_info->details->text_keep_upright)
     649                 :            :         {
     650                 :          0 :           avg_angle = shumate_vector_point_iter_get_average_angle (&iter, self->layout_width / tile_size_for_zoom);
     651                 :          0 :           avg_angle = positive_mod (avg_angle + rotation, G_PI * 2.0);
     652   [ #  #  #  # ]:          0 :           if (avg_angle > G_PI / 2.0 && avg_angle < 3.0 * G_PI / 2.0)
     653                 :            :             {
     654                 :          0 :               iter.reversed = TRUE;
     655                 :          0 :               iter.current_point = iter.num_points - 1;
     656                 :          0 :               iter.distance = 0;
     657                 :          0 :               shumate_vector_point_iter_advance (&iter, self->line_length - start_pos - (self->layout_width / tile_size_for_zoom));
     658                 :            :             }
     659                 :            :         }
     660                 :            : 
     661                 :          0 :       gtk_snapshot_save (snapshot);
     662                 :          0 :       gtk_snapshot_rotate (snapshot, rotation * 180 / G_PI);
     663                 :            : 
     664         [ #  # ]:          0 :       if (self->symbol_info->details->text_opacity < 1.0)
     665                 :          0 :         gtk_snapshot_push_opacity (snapshot, self->symbol_info->details->text_opacity);
     666                 :            : 
     667         [ #  # ]:          0 :       for (int i = 0; i < self->glyphs->len; i ++)
     668                 :            :         {
     669                 :          0 :           Glyph *glyph = &((Glyph *)self->glyphs->data)[i];
     670                 :          0 :           ShumateVectorPoint point;
     671                 :            : 
     672                 :            :           /* Whitespace has no glyph, but still has a width that needs to be
     673                 :            :            * advanced in the point iter */
     674   [ #  #  #  # ]:          0 :           if (glyph->node != NULL || glyph->sprite != NULL)
     675                 :          0 :             {
     676                 :          0 :               double angle;
     677                 :            : 
     678         [ #  # ]:          0 :               if (self->symbol_info->details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_VIEWPORT_GLYPH)
     679                 :          0 :                 angle = -rotation;
     680                 :            :               else
     681                 :          0 :                 angle = shumate_vector_point_iter_get_average_angle (&iter, glyph->width / tile_size_for_zoom);
     682                 :            : 
     683                 :          0 :               shumate_vector_point_iter_advance (&iter, glyph->width / tile_size_for_zoom / 2.0);
     684                 :          0 :               shumate_vector_point_iter_get_current_point (&iter, &point);
     685                 :          0 :               shumate_vector_point_iter_advance (&iter, glyph->width / tile_size_for_zoom / 2.0);
     686                 :            : 
     687                 :          0 :               gtk_snapshot_save (snapshot);
     688                 :          0 :               gtk_snapshot_translate (snapshot,
     689                 :          0 :                                       &GRAPHENE_POINT_INIT (
     690                 :            :                                         (point.x - self->symbol_info->x) * tile_size_for_zoom,
     691                 :            :                                         (point.y - self->symbol_info->y) * tile_size_for_zoom
     692                 :            :                                       ));
     693                 :          0 :               gtk_snapshot_rotate (snapshot, angle * 180 / G_PI);
     694                 :          0 :               gtk_snapshot_translate (snapshot,
     695                 :          0 :                                       &GRAPHENE_POINT_INIT (-glyph->width / 2.0,
     696                 :            :                                                             self->baseline - self->layout_y - self->layout_height / 2.0));
     697                 :            : 
     698         [ #  # ]:          0 :               if (glyph->node != NULL)
     699                 :          0 :                 gtk_snapshot_append_node (snapshot, glyph->node);
     700                 :            :               else
     701                 :            :                 {
     702                 :          0 :                   int width = shumate_vector_sprite_get_width (glyph->sprite);
     703                 :          0 :                   int height = shumate_vector_sprite_get_height (glyph->sprite);
     704                 :          0 :                   gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, -height));
     705                 :          0 :                   gtk_symbolic_paintable_snapshot_symbolic (
     706                 :          0 :                     GTK_SYMBOLIC_PAINTABLE (glyph->sprite),
     707                 :            :                     snapshot,
     708                 :            :                     width,
     709                 :            :                     height,
     710                 :          0 :                     &glyph->icon_color,
     711                 :            :                     1
     712                 :            :                   );
     713                 :            :                 }
     714                 :            : 
     715                 :          0 :               gtk_snapshot_restore (snapshot);
     716                 :            :             }
     717                 :            :           else
     718                 :          0 :             shumate_vector_point_iter_advance (&iter, glyph->width / tile_size_for_zoom);
     719                 :            :         }
     720                 :            : 
     721         [ #  # ]:          0 :       if (self->symbol_info->details->text_opacity < 1.0)
     722                 :          0 :         gtk_snapshot_pop (snapshot);
     723                 :            : 
     724                 :          0 :       gtk_snapshot_restore (snapshot);
     725                 :            :     }
     726   [ #  #  #  #  :          0 :   else if (self->show_text && self->glyphs_node && self->symbol_info->details->text_opacity > 0.0)
                   #  # ]
     727                 :            :     {
     728                 :          0 :       double angle = 0;
     729                 :          0 :       double offset_x = self->symbol_info->details->text_offset_x * self->symbol_info->details->text_size;
     730                 :          0 :       double offset_y = self->symbol_info->details->text_offset_y * self->symbol_info->details->text_size;
     731                 :            : 
     732                 :          0 :       add_anchor_offset (self->symbol_info->details->text_anchor,
     733                 :            :                          &offset_x, &offset_y,
     734                 :          0 :                          self->layout_width, self->layout_height);
     735                 :            : 
     736         [ #  # ]:          0 :       if (self->symbol_info->details->text_rotation_alignment != SHUMATE_VECTOR_ALIGNMENT_MAP)
     737                 :          0 :         angle = -rotation;
     738                 :            : 
     739                 :          0 :       gtk_snapshot_save (snapshot);
     740                 :            : 
     741         [ #  # ]:          0 :       if (self->symbol_info->details->text_opacity < 1)
     742                 :          0 :         gtk_snapshot_push_opacity (snapshot, self->symbol_info->details->text_opacity);
     743                 :            : 
     744                 :          0 :       gtk_snapshot_rotate (snapshot, rotation * 180 / G_PI);
     745                 :          0 :       gtk_snapshot_translate (snapshot,
     746                 :          0 :                               &GRAPHENE_POINT_INIT (
     747                 :            :                                 self->midpoint.x * tile_size_for_zoom,
     748                 :            :                                 self->midpoint.y * tile_size_for_zoom
     749                 :            :                               ));
     750                 :          0 :       gtk_snapshot_rotate (snapshot, angle * 180 / G_PI);
     751                 :            : 
     752                 :          0 :       gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (
     753                 :            :                                           -self->layout_width / 2.0 + offset_x,
     754                 :            :                                           -self->layout_y - self->layout_height / 2.0 + offset_y
     755                 :            :                                         ));
     756                 :          0 :       gtk_snapshot_append_node (snapshot, self->glyphs_node);
     757                 :            : 
     758         [ #  # ]:          0 :       if (self->symbol_info->details->text_opacity < 1)
     759                 :          0 :         gtk_snapshot_pop (snapshot);
     760                 :            : 
     761                 :          0 :       gtk_snapshot_restore (snapshot);
     762                 :            :     }
     763                 :            : 
     764                 :          0 :   gtk_snapshot_restore (snapshot);
     765                 :          0 : }
     766                 :            : 
     767                 :            : 
     768                 :            : static void
     769                 :          0 : shumate_vector_symbol_class_init (ShumateVectorSymbolClass *klass)
     770                 :            : {
     771                 :          0 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     772                 :          0 :   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
     773                 :            : 
     774                 :          0 :   object_class->constructed = shumate_vector_symbol_constructed;
     775                 :          0 :   object_class->dispose = shumate_vector_symbol_dispose;
     776                 :          0 :   object_class->get_property = shumate_vector_symbol_get_property;
     777                 :          0 :   object_class->set_property = shumate_vector_symbol_set_property;
     778                 :            : 
     779                 :          0 :   widget_class->contains = shumate_vector_symbol_contains;
     780                 :          0 :   widget_class->get_request_mode = shumate_vector_symbol_get_request_mode;
     781                 :          0 :   widget_class->snapshot = shumate_vector_symbol_snapshot;
     782                 :          0 :   widget_class->measure = shumate_vector_symbol_measure;
     783                 :            : 
     784                 :          0 :   obj_properties[PROP_SYMBOL_INFO] =
     785                 :          0 :     g_param_spec_boxed ("symbol-info",
     786                 :            :                         "Symbol info",
     787                 :            :                         "Symbol info",
     788                 :            :                         SHUMATE_TYPE_VECTOR_SYMBOL_INFO,
     789                 :            :                         G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
     790                 :            : 
     791                 :          0 :   g_object_class_install_properties (object_class, N_PROPS, obj_properties);
     792                 :            : 
     793                 :          0 :   signals[CLICKED] =
     794                 :          0 :     g_signal_new ("clicked",
     795                 :            :                   G_OBJECT_CLASS_TYPE (object_class),
     796                 :            :                   G_SIGNAL_RUN_LAST,
     797                 :            :                   0, NULL, NULL,
     798                 :            :                   g_cclosure_marshal_VOID__OBJECT,
     799                 :            :                   G_TYPE_NONE,
     800                 :            :                   1,
     801                 :            :                   SHUMATE_TYPE_SYMBOL_EVENT);
     802                 :            : 
     803                 :          0 :   gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_LABEL);
     804                 :          0 : }
     805                 :            : 
     806                 :            : static void
     807                 :          0 : on_clicked (ShumateVectorSymbol *self,
     808                 :            :             gint                 n_press,
     809                 :            :             double               x,
     810                 :            :             double               y,
     811                 :            :             GtkGestureClick     *click)
     812                 :            : {
     813                 :          0 :   g_autoptr(ShumateSymbolEvent) event = shumate_symbol_event_new (self->symbol_info->details->layer,
     814                 :          0 :                                                                   self->symbol_info->details->source_layer,
     815                 :          0 :                                                                   self->symbol_info->details->feature_id,
     816                 :          0 :                                                                   self->symbol_info->details->tags);
     817         [ #  # ]:          0 :   g_signal_emit (self, signals[CLICKED], 0, event);
     818                 :          0 : }
     819                 :            : 
     820                 :            : static void
     821                 :          0 : shumate_vector_symbol_init (ShumateVectorSymbol *self)
     822                 :            : {
     823                 :          0 :   GtkGesture *click = gtk_gesture_click_new ();
     824                 :          0 :   g_signal_connect_object (click, "released", G_CALLBACK (on_clicked), self, G_CONNECT_SWAPPED);
     825                 :          0 :   gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (click));
     826                 :          0 : }
     827                 :            : 
     828                 :            : 
     829                 :            : ShumateVectorSymbolInfo *
     830                 :          0 : shumate_vector_symbol_get_symbol_info (ShumateVectorSymbol *self)
     831                 :            : {
     832         [ #  # ]:          0 :   g_return_val_if_fail (SHUMATE_IS_VECTOR_SYMBOL (self), NULL);
     833                 :            : 
     834                 :          0 :   return self->symbol_info;
     835                 :            : }
     836                 :            : 
     837                 :            : static void
     838                 :          0 : rotate_around_center (double *x,
     839                 :            :                       double *y,
     840                 :            :                       double  angle)
     841                 :            : {
     842                 :            :   /* Rotate (x, y) around (0, 0) */
     843                 :            : 
     844         [ #  # ]:          0 :   if (angle == 0)
     845                 :            :     return;
     846                 :            : 
     847                 :          0 :   double old_x = *x;
     848                 :          0 :   double old_y = *y;
     849                 :            : 
     850                 :          0 :   *x = cosf (angle) * old_x - sinf (angle) * old_y;
     851                 :          0 :   *y = sinf (angle) * old_x + cosf (angle) * old_y;
     852                 :            : }
     853                 :            : 
     854                 :            : 
     855                 :            : gboolean
     856                 :          0 : shumate_vector_symbol_calculate_collision (ShumateVectorSymbol    *self,
     857                 :            :                                            ShumateVectorCollision *collision,
     858                 :            :                                            double                  x,
     859                 :            :                                            double                  y,
     860                 :            :                                            double                  tile_size_for_zoom,
     861                 :            :                                            double                  rotation,
     862                 :            :                                            graphene_rect_t        *bounds_out)
     863                 :            : {
     864                 :          0 :   double yextent = self->symbol_info->details->text_size / 2.0;
     865                 :          0 :   gboolean check;
     866                 :          0 :   ShumateVectorPointIter iter;
     867                 :          0 :   ShumateVectorPoint point;
     868                 :          0 :   ShumateVectorPoint midpoint = {0};
     869                 :          0 :   int save = 0;
     870                 :            : 
     871                 :          0 :   shumate_vector_collision_rollback_pending (collision, 0);
     872                 :            : 
     873         [ #  # ]:          0 :   if (self->symbol_info->line != NULL)
     874                 :            :     {
     875                 :          0 :       midpoint = self->midpoint;
     876                 :            : 
     877                 :          0 :       rotate_around_center (&midpoint.x, &midpoint.y, rotation);
     878                 :          0 :       midpoint.x *= tile_size_for_zoom;
     879                 :          0 :       midpoint.y *= tile_size_for_zoom;
     880                 :            :     }
     881                 :            : 
     882         [ #  # ]:          0 :   if (self->glyphs != NULL)
     883                 :            :     {
     884                 :          0 :       double line_length = self->symbol_info->line_length;
     885                 :          0 :       double length = self->layout_width / tile_size_for_zoom;
     886         [ #  # ]:          0 :       double start_pos = MAX (0, self->symbol_info->line_position - (length / 2.0));
     887                 :            : 
     888         [ #  # ]:          0 :       g_assert (self->symbol_info->line != NULL);
     889                 :            : 
     890         [ #  # ]:          0 :       if (length > line_length - start_pos)
     891                 :            :         {
     892                 :          0 :           self->show_text = FALSE;
     893                 :          0 :           return FALSE;
     894                 :            :         }
     895                 :            : 
     896                 :          0 :       shumate_vector_point_iter_init (&iter, self->symbol_info->line);
     897                 :          0 :       shumate_vector_point_iter_advance (&iter, start_pos);
     898                 :            : 
     899                 :          0 :       self->show_text = TRUE;
     900                 :            : 
     901                 :          0 :       do
     902                 :            :         {
     903         [ #  # ]:          0 :           double xextent = MIN (length, shumate_vector_point_iter_get_segment_length (&iter) - iter.distance) * tile_size_for_zoom / 2;
     904                 :            : 
     905         [ #  # ]:          0 :           if (shumate_vector_point_iter_is_at_end (&iter))
     906                 :            :             return FALSE;
     907                 :            : 
     908                 :          0 :           shumate_vector_point_iter_get_segment_center (&iter, length, &point);
     909                 :          0 :           point.x -= self->symbol_info->x;
     910                 :          0 :           point.y -= self->symbol_info->y;
     911                 :            : 
     912                 :          0 :           rotate_around_center (&point.x, &point.y, rotation);
     913                 :          0 :           point.x *= tile_size_for_zoom;
     914                 :          0 :           point.y *= tile_size_for_zoom;
     915                 :            : 
     916                 :          0 :           gboolean check = shumate_vector_collision_check (
     917                 :            :             collision,
     918                 :          0 :             x + point.x,
     919                 :          0 :             y + point.y,
     920                 :            :             xextent + self->symbol_info->details->text_padding,
     921                 :          0 :             yextent + self->symbol_info->details->text_padding,
     922                 :          0 :             rotation + shumate_vector_point_iter_get_current_angle (&iter),
     923                 :            :             self->symbol_info->details->text_overlap,
     924                 :          0 :             self->symbol_info->details->text_ignore_placement,
     925                 :            :             self
     926                 :            :           );
     927                 :            : 
     928         [ #  # ]:          0 :           if (!check)
     929                 :            :             {
     930         [ #  # ]:          0 :               if (self->symbol_info->details->text_optional)
     931                 :            :                 {
     932                 :          0 :                   shumate_vector_collision_rollback_pending (collision, save);
     933                 :          0 :                   self->show_text = FALSE;
     934                 :          0 :                   break;
     935                 :            :                 }
     936                 :            :               else
     937                 :            :                 return FALSE;
     938                 :            :             }
     939                 :            :         }
     940         [ #  # ]:          0 :       while ((length -= shumate_vector_point_iter_next_segment (&iter)) > 0);
     941                 :            :     }
     942         [ #  # ]:          0 :   else if (self->glyphs_node != NULL)
     943                 :            :     {
     944                 :          0 :       ShumateVectorAnchor anchor = self->symbol_info->details->text_anchor;
     945                 :          0 :       double angle = 0;
     946                 :          0 :       double offset_x = self->symbol_info->details->text_offset_x * self->symbol_info->details->text_size;
     947                 :          0 :       double offset_y = self->symbol_info->details->text_offset_y * self->symbol_info->details->text_size;
     948                 :            : 
     949                 :          0 :       add_anchor_offset (anchor, &offset_x, &offset_y, self->layout_width, self->layout_height);
     950                 :            : 
     951         [ #  # ]:          0 :       if (self->symbol_info->details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_MAP)
     952                 :          0 :         angle = rotation;
     953                 :            : 
     954                 :          0 :       rotate_around_center (&offset_x, &offset_y, angle);
     955                 :            : 
     956                 :          0 :       self->show_text = TRUE;
     957                 :            : 
     958                 :          0 :       check = shumate_vector_collision_check (
     959                 :            :         collision,
     960                 :          0 :         x + midpoint.x + offset_x,
     961                 :          0 :         y + midpoint.y + offset_y,
     962                 :          0 :         self->layout_width / 2.0 + self->symbol_info->details->text_padding,
     963                 :          0 :         yextent + self->symbol_info->details->text_padding,
     964                 :            :         angle,
     965                 :            :         self->symbol_info->details->text_overlap,
     966                 :          0 :         self->symbol_info->details->text_ignore_placement,
     967                 :            :         self
     968                 :            :       );
     969                 :            : 
     970         [ #  # ]:          0 :       if (!check)
     971                 :            :         {
     972         [ #  # ]:          0 :           if (self->symbol_info->details->text_optional)
     973                 :            :             {
     974                 :          0 :               shumate_vector_collision_rollback_pending (collision, save);
     975                 :          0 :               self->show_text = FALSE;
     976                 :            :             }
     977                 :            :           else
     978                 :          0 :             return FALSE;
     979                 :            :         }
     980                 :            :     }
     981                 :            : 
     982                 :          0 :   save = shumate_vector_collision_save_pending (collision);
     983                 :            : 
     984         [ #  # ]:          0 :   if (self->symbol_info->details->icon_image)
     985                 :            :     {
     986                 :          0 :       float angle = 0;
     987                 :          0 :       double icon_width = shumate_vector_sprite_get_width (self->symbol_info->details->icon_image)
     988                 :          0 :                           * self->symbol_info->details->icon_size;
     989                 :          0 :       double icon_height = shumate_vector_sprite_get_height (self->symbol_info->details->icon_image)
     990                 :          0 :                            * self->symbol_info->details->icon_size;
     991                 :            : 
     992                 :          0 :       double offset_x = self->symbol_info->details->icon_offset_x * self->symbol_info->details->icon_size;
     993                 :          0 :       double offset_y = self->symbol_info->details->icon_offset_y * self->symbol_info->details->icon_size;
     994                 :            : 
     995                 :          0 :       self->show_icon = TRUE;
     996                 :            : 
     997                 :          0 :       add_anchor_offset (self->symbol_info->details->icon_anchor, &offset_x, &offset_y, icon_width, icon_height);
     998                 :            : 
     999         [ #  # ]:          0 :       if (self->symbol_info->details->icon_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_MAP)
    1000                 :          0 :         angle = rotation + self->midpoint_angle;
    1001                 :          0 :       angle += self->symbol_info->details->icon_rotate;
    1002                 :          0 :       offset_x += (self->symbol_info->details->icon_padding_right - self->symbol_info->details->icon_padding_left) / 2.0;
    1003                 :          0 :       offset_y += (self->symbol_info->details->icon_padding_bottom - self->symbol_info->details->icon_padding_top) / 2.0;
    1004                 :          0 :       rotate_around_center (&offset_x, &offset_y, angle);
    1005                 :            : 
    1006                 :          0 :       check = shumate_vector_collision_check (
    1007                 :            :         collision,
    1008                 :          0 :         x + midpoint.x + offset_x,
    1009                 :          0 :         y + midpoint.y + offset_y,
    1010                 :          0 :         (icon_width + self->symbol_info->details->icon_padding_left + self->symbol_info->details->icon_padding_right) / 2 ,
    1011                 :          0 :         (icon_height + self->symbol_info->details->icon_padding_top + self->symbol_info->details->icon_padding_bottom) / 2,
    1012                 :            :         angle,
    1013                 :            :         self->symbol_info->details->icon_overlap,
    1014                 :          0 :         self->symbol_info->details->icon_ignore_placement,
    1015                 :            :         self
    1016                 :            :       );
    1017                 :            : 
    1018         [ #  # ]:          0 :       if (!check)
    1019                 :            :         {
    1020         [ #  # ]:          0 :           if (self->symbol_info->details->icon_optional)
    1021                 :            :             {
    1022                 :          0 :               shumate_vector_collision_rollback_pending (collision, save);
    1023                 :          0 :               self->show_icon = FALSE;
    1024                 :            :             }
    1025                 :            :           else
    1026                 :          0 :             return FALSE;
    1027                 :            :         }
    1028                 :            :     }
    1029                 :            : 
    1030         [ #  # ]:          0 :   if (!self->show_icon && !self->show_text)
    1031                 :            :     return FALSE;
    1032                 :            : 
    1033                 :          0 :   shumate_vector_collision_commit_pending (collision, &self->bounds);
    1034                 :          0 :   *bounds_out = self->bounds;
    1035                 :          0 :   self->x = x;
    1036                 :          0 :   self->y = y;
    1037                 :          0 :   return TRUE;
    1038                 :            : }
    1039                 :            : 

Generated by: LCOV version 1.14