[libshumate] vector: Calculate rectangle intersection differently



commit 68a8a2cf9faa790a0c1d68ee7723f816b934f91d
Author: James Westman <james jwestman net>
Date:   Wed Mar 30 00:46:44 2022 -0500

    vector: Calculate rectangle intersection differently
    
    Use the Separating Axis Theorem to calculate rectangle intersection so
    we can handle rotated bounding boxes correctly.

 shumate/vector/shumate-vector-collision-private.h  |  22 +-
 shumate/vector/shumate-vector-collision.c          | 285 ++++++++++++---------
 shumate/vector/shumate-vector-render-scope.c       |   9 +-
 shumate/vector/shumate-vector-symbol-container.c   |  21 +-
 .../vector/shumate-vector-symbol-info-private.h    |   1 +
 shumate/vector/shumate-vector-symbol-info.c        |   4 +
 shumate/vector/shumate-vector-symbol-layer.c       |   3 +
 shumate/vector/shumate-vector-symbol.c             |   6 +
 shumate/vector/shumate-vector-utils-private.h      |   4 +-
 shumate/vector/shumate-vector-utils.c              |  28 ++
 tests/vector-collision.c                           |  35 ++-
 11 files changed, 262 insertions(+), 156 deletions(-)
---
diff --git a/shumate/vector/shumate-vector-collision-private.h 
b/shumate/vector/shumate-vector-collision-private.h
index 4a3437f..33da298 100644
--- a/shumate/vector/shumate-vector-collision-private.h
+++ b/shumate/vector/shumate-vector-collision-private.h
@@ -26,21 +26,22 @@ G_BEGIN_DECLS
 typedef struct ShumateVectorCollision ShumateVectorCollision;
 
 typedef struct {
-  float left;
-  float right;
-  float top;
-  float bottom;
-} ShumateVectorCollisionRect;
+  float x;
+  float y;
+  float xextent;
+  float yextent;
+  float rotation;
+} ShumateVectorCollisionBBox;
 
 typedef struct {
-  ShumateVectorCollisionRect rect;
-  ShumateVectorPoint center;
+  ShumateVectorCollisionBBox bbox;
   GList *list_link;
   float x;
   float y;
   int zoom;
   guint seq : 1;
   guint visible : 1;
+  guint rotates : 1;
 } ShumateVectorCollisionMarker;
 
 ShumateVectorCollision *shumate_vector_collision_new ();
@@ -50,10 +51,9 @@ ShumateVectorCollisionMarker *shumate_vector_collision_insert (ShumateVectorColl
                                                                int                     zoom,
                                                                float                   x,
                                                                float                   y,
-                                                               float                   left,
-                                                               float                   right,
-                                                               float                   top,
-                                                               float                   bottom);
+                                                               float                   xextent,
+                                                               float                   yextent,
+                                                               guint                   rotates);
 
 void shumate_vector_collision_remove (ShumateVectorCollision       *self,
                                       ShumateVectorCollisionMarker *marker);
diff --git a/shumate/vector/shumate-vector-collision.c b/shumate/vector/shumate-vector-collision.c
index 4899b6b..4682a83 100644
--- a/shumate/vector/shumate-vector-collision.c
+++ b/shumate/vector/shumate-vector-collision.c
@@ -52,31 +52,31 @@ struct ShumateVectorCollision {
 
 typedef struct {
   GPtrArray *markers;
-  ShumateVectorCollisionRect rect;
+  ShumateVectorCollisionBBox bbox;
 } RTreeCol;
 
 typedef struct {
   RTreeCol cols[NODES];
-  ShumateVectorCollisionRect rect;
+  ShumateVectorCollisionBBox bbox;
 } RTreeRow;
 
 typedef struct {
   RTreeRow rows[NODES];
-  ShumateVectorCollisionRect rect;
+  ShumateVectorCollisionBBox bbox;
   int n_markers;
 } RTreeTileCol;
 
 typedef struct {
   GHashTable *tile_cols;
-  ShumateVectorCollisionRect rect;
+  ShumateVectorCollisionBBox bbox;
 } RTreeTileRow;
 
 
 static RTreeTileCol *
-tile_col_new (ShumateVectorCollisionRect *rect)
+tile_col_new (ShumateVectorCollisionBBox *bbox)
 {
   RTreeTileCol *tile_col = g_new0 (RTreeTileCol, 1);
-  tile_col->rect = *rect;
+  tile_col->bbox = *bbox;
   return tile_col;
 }
 
@@ -93,11 +93,11 @@ tile_col_free (RTreeTileCol *tile_col)
 
 
 static RTreeTileRow *
-tile_row_new (ShumateVectorCollisionRect *rect)
+tile_row_new (ShumateVectorCollisionBBox *bbox)
 {
   RTreeTileRow *tile_row = g_new0 (RTreeTileRow, 1);
   tile_row->tile_cols = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) 
tile_col_free);
-  tile_row->rect = *rect;
+  tile_row->bbox = *bbox;
   return tile_row;
 }
 
@@ -136,104 +136,164 @@ shumate_vector_collision_free (ShumateVectorCollision *self)
 }
 
 
-static gboolean
-rects_intersect (ShumateVectorCollisionRect *a,
-                 ShumateVectorCollisionRect *b)
+static float
+dot (ShumateVectorPoint *a,
+     ShumateVectorPoint *b)
 {
-  g_assert (a->left <= a->right);
-  g_assert (a->top <= a->bottom);
-  g_assert (b->left <= b->right);
-  g_assert (b->top <= b->bottom);
-
-  return !(a->left >= b->right
-           || a->right <= b->left
-           || a->top >= b->bottom
-           || a->bottom <= b->top);
+  return a->x * b->x + a->y * b->y;
 }
 
 
-static void
-expand_rect (ShumateVectorCollisionRect       *a,
-             const ShumateVectorCollisionRect *b)
+static float
+project (ShumateVectorPoint *point,
+         ShumateVectorPoint *axis)
 {
-  if (a->left == 0 && a->right == 0 && a->top == 0 && a->bottom == 0)
-    *a = *b;
-  else
-    {
-      a->left = MIN (a->left, b->left);
-      a->right = MAX (a->right, b->right);
-      a->top = MIN (a->top, b->top);
-      a->bottom = MAX (a->bottom, b->bottom);
-    }
+  ShumateVectorPoint tmp;
+  float s = dot (point, axis) / LEN_SQ (axis->x, axis->y);
+  tmp = *axis;
+  tmp.x *= s;
+  tmp.y *= s;
+  return dot (&tmp, axis);
 }
 
 
-static void
-rotate_point (ShumateVectorPoint *point,
-              float               radians)
+static ShumateVectorPoint
+corner (float x,
+        float y,
+        float xextent,
+        float yextent,
+        float rot_cos,
+        float rot_sin)
 {
-  float x, y, s, c;
+  return (ShumateVectorPoint) {
+    .x = xextent * rot_cos - yextent * rot_sin + x,
+    .y = xextent * rot_sin + yextent * rot_cos + y,
+  };
+}
 
-  if (radians == 0)
-    return;
 
-  x = point->x;
-  y = point->y;
-  s = sin (-radians);
-  c = cos (-radians);
+static gboolean
+rects_intersect (ShumateVectorCollisionBBox *a,
+                 ShumateVectorCollisionBBox *b)
+{
+  float cos_a, sin_a, cos_b, sin_b;
+  ShumateVectorPoint axes[4], corners_a[4], corners_b[4];
+
+  if (a->rotation == 0 && b->rotation == 0)
+    {
+      return !((a->x + a->xextent < b->x - b->xextent)
+                || (b->x + b->xextent < a->x - a->xextent)
+                || (a->y + a->yextent < b->y - b->yextent)
+                || (b->y + b->yextent < a->y - a->yextent));
+    }
+
+  /* See 
<https://www.gamedev.net/articles/programming/general-and-gameplay-programming/2d-rotated-rectangle-collision-r2604/>
 */
+
+  cos_a = cosf (a->rotation);
+  sin_a = sinf (a->rotation);
+  cos_b = cosf (b->rotation);
+  sin_b = sinf (b->rotation);
+
+  /* Calculate the four axes of the two rectangles */
+  axes[0] = (ShumateVectorPoint) { cos_a, sin_a };
+  axes[1] = (ShumateVectorPoint) { -sin_a, cos_a };
+  axes[2] = (ShumateVectorPoint) { cos_b, sin_b };
+  axes[3] = (ShumateVectorPoint) { -sin_b, cos_b };
+
+  corners_a[0] = corner (a->x, a->y, a->xextent, a->yextent, cos_a, sin_a);
+  corners_a[1] = corner (a->x, a->y, -a->xextent, a->yextent, cos_a, sin_a);
+  corners_a[2] = corner (a->x, a->y, a->xextent, -a->yextent, cos_a, sin_a);
+  corners_a[3] = corner (a->x, a->y, -a->xextent, -a->yextent, cos_a, sin_a);
+
+  corners_b[0] = corner (b->x, b->y, b->xextent, b->yextent, cos_b, sin_b);
+  corners_b[1] = corner (b->x, b->y, -b->xextent, b->yextent, cos_b, sin_b);
+  corners_b[2] = corner (b->x, b->y, b->xextent, -b->yextent, cos_b, sin_b);
+  corners_b[3] = corner (b->x, b->y, -b->xextent, -b->yextent, cos_b, sin_b);
+
+  for (int i = 0; i < 4; i ++)
+    {
+      ShumateVectorPoint *axis = &axes[i];
+
+      float proj_a[4], proj_b[4];
+
+      /* Project the corners of the rectangles onto the axis */
+      for (int j = 0; j < 4; j ++)
+        {
+          proj_a[j] = project (&corners_a[j], axis);
+          proj_b[j] = project (&corners_b[j], axis);
+        }
+
+      /* If the projected points don't overlap, the rectangles don't overlap
+       * (i.e. either every item in proj_a is greater than or is less than every
+       * item in proj_b). */
+      float min_a = MIN4 (proj_a[0], proj_a[1], proj_a[2], proj_a[3]);
+      float max_a = MAX4 (proj_a[0], proj_a[1], proj_a[2], proj_a[3]);
+      float min_b = MIN4 (proj_b[0], proj_b[1], proj_b[2], proj_b[3]);
+      float max_b = MAX4 (proj_b[0], proj_b[1], proj_b[2], proj_b[3]);
+
+      if (min_a >= max_b || min_b >= max_a)
+        return FALSE;
+    }
 
-  point->x = x * c - y * s;
-  point->y = x * s + y * c;
+  return TRUE;
 }
 
 
 static void
-scale_point (ShumateVectorPoint *point,
-             float               scale)
+expand_rect (ShumateVectorCollisionBBox       *a,
+             const ShumateVectorCollisionBBox *b)
 {
-  point->x *= scale;
-  point->y *= scale;
+  g_assert (a->rotation == 0);
+  g_assert (b->rotation == 0);
+
+  if (a->x == 0 && a->y == 0 && a->xextent == 0 && a->yextent == 0)
+    *a = *b;
+  else
+    {
+      float left = MIN (a->x - a->xextent, b->x - b->xextent);
+      float right = MAX (a->x + a->xextent, b->x + b->xextent);
+      float top = MIN (a->y - a->yextent, b->y - b->yextent);
+      float bottom = MAX (a->y + a->yextent, b->y + b->yextent);
+      a->x = (left + right) / 2.0;
+      a->y = (top + bottom) / 2.0;
+      a->xextent = (right - left) / 2.0;
+      a->yextent = (bottom - top) / 2.0;
+    }
 }
 
 
 static void
-get_marker_bbox (ShumateVectorCollisionMarker *marker,
-                 float                         rot,
-                 float                         zoom,
-                 ShumateVectorCollisionRect   *rect_out)
+get_marker_full_rot_bbox (ShumateVectorCollisionMarker *marker,
+                          ShumateVectorCollisionBBox   *bbox_out)
 {
-  ShumateVectorPoint top_left = { marker->rect.left, marker->rect.top };
-  ShumateVectorPoint top_right = { marker->rect.right, marker->rect.top };
-  ShumateVectorPoint bottom_left = { marker->rect.left, marker->rect.bottom };
-  ShumateVectorPoint bottom_right = { marker->rect.right, marker->rect.bottom };
-
-  rotate_point (&top_left, rot);
-  rotate_point (&top_right, rot);
-  rotate_point (&bottom_left, rot);
-  rotate_point (&bottom_right, rot);
-
-  rect_out->left = MIN4 (top_left.x, top_right.x, bottom_left.x, bottom_right.x) + marker->center.x;
-  rect_out->right = MAX4 (top_left.x, top_right.x, bottom_left.x, bottom_right.x) + marker->center.x;
-  rect_out->top = MIN4 (top_left.y, top_right.y, bottom_left.y, bottom_right.y) + marker->center.y;
-  rect_out->bottom = MAX4 (top_left.y, top_right.y, bottom_left.y, bottom_right.y) + marker->center.y;
+  float radius = sqrt (LEN_SQ (marker->bbox.xextent, marker->bbox.yextent));
+
+  if (marker->rotates)
+    {
+      bbox_out->x = marker->bbox.x;
+      bbox_out->y = marker->bbox.y;
+      bbox_out->xextent = radius;
+      bbox_out->yextent = radius;
+      bbox_out->rotation = 0;
+    }
+  else
+    *bbox_out = marker->bbox;
 }
 
 
 static void
-get_marker_full_rot_bbox (ShumateVectorCollisionMarker *marker,
-                          ShumateVectorCollisionRect   *rect_out)
+get_marker_bbox (ShumateVectorCollisionMarker *marker,
+                 float                         rot,
+                 float                         zoom,
+                 ShumateVectorCollisionBBox   *bbox)
 {
-  float top_left = LEN_SQ (marker->rect.top, marker->rect.left);
-  float bottom_left = LEN_SQ (marker->rect.bottom, marker->rect.left);
-  float top_right = LEN_SQ (marker->rect.top, marker->rect.right);
-  float bottom_right = LEN_SQ (marker->rect.bottom, marker->rect.right);
+  float scale = powf (2, zoom - marker->zoom);
 
-  float radius = sqrt (MAX4 (top_left, bottom_left, top_right, bottom_right));
-
-  rect_out->left = marker->center.x - radius;
-  rect_out->right = marker->center.x + radius;
-  rect_out->top = marker->center.y - radius;
-  rect_out->bottom = marker->center.y + radius;
+  *bbox = marker->bbox;
+  bbox->x *= scale;
+  bbox->y *= scale;
+  if (marker->rotates)
+    bbox->rotation -= rot;
 }
 
 
@@ -243,23 +303,12 @@ markers_intersect (ShumateVectorCollisionMarker *a,
                    float                         rot,
                    float                         zoom)
 {
-  ShumateVectorPoint a_center = a->center;
-  ShumateVectorPoint b_center = b->center;
-
-  g_assert (a->rect.left <= a->rect.right);
-  g_assert (a->rect.top <= a->rect.bottom);
-  g_assert (b->rect.left <= b->rect.right);
-  g_assert (b->rect.top <= b->rect.bottom);
-
-  rotate_point (&a_center, -rot);
-  rotate_point (&b_center, -rot);
-  scale_point (&a_center, powf (2, zoom - a->zoom));
-  scale_point (&b_center, powf (2, zoom - b->zoom));
-
-  return !(a_center.x + a->rect.left >= b_center.x + b->rect.right
-           || a_center.x + a->rect.right <= b_center.x + b->rect.left
-           || a_center.y + a->rect.top >= b_center.y + b->rect.bottom
-           || a_center.y + a->rect.bottom <= b_center.y + b->rect.top);
+  ShumateVectorCollisionBBox a_bbox, b_bbox;
+
+  get_marker_bbox (a, rot, zoom, &a_bbox);
+  get_marker_bbox (b, rot, zoom, &b_bbox);
+
+  return rects_intersect (&a_bbox, &b_bbox);
 }
 
 
@@ -282,17 +331,16 @@ shumate_vector_collision_insert (ShumateVectorCollision *self,
                                  int                     zoom,
                                  float                   x,
                                  float                   y,
-                                 float                   left,
-                                 float                   right,
-                                 float                   top,
-                                 float                   bottom)
+                                 float                   xextent,
+                                 float                   yextent,
+                                 guint                   rotates)
 {
   RTreeTileRow *tile_row;
   RTreeTileCol *tile_col;
   RTreeRow *row;
   RTreeCol *col;
+  ShumateVectorCollisionBBox bbox;
   ShumateVectorCollisionMarker *marker;
-  ShumateVectorCollisionRect bbox;
   gint64 tile_x = floor (x / TILE_SIZE);
   gint64 tile_y = floor (y / TILE_SIZE);
 
@@ -301,12 +349,11 @@ shumate_vector_collision_insert (ShumateVectorCollision *self,
   zoom = CLAMP (zoom, 0, ZOOM_LAYERS - 1);
 
   marker = g_new0 (ShumateVectorCollisionMarker, 1);
-  marker->center.x = x;
-  marker->center.y = y;
-  marker->rect.left = left;
-  marker->rect.right = right;
-  marker->rect.top = top;
-  marker->rect.bottom = bottom;
+  marker->bbox.x = x;
+  marker->bbox.y = y;
+  marker->bbox.xextent = xextent;
+  marker->bbox.yextent = yextent;
+  marker->rotates = !!rotates;
   marker->zoom = zoom;
   marker->seq = self->seq;
 
@@ -339,10 +386,10 @@ shumate_vector_collision_insert (ShumateVectorCollision *self,
   tile_col->n_markers ++;
 
   /* Expand the parents to fit the new marker */
-  expand_rect (&tile_row->rect, &bbox);
-  expand_rect (&tile_col->rect, &bbox);
-  expand_rect (&row->rect, &bbox);
-  expand_rect (&col->rect, &bbox);
+  expand_rect (&tile_row->bbox, &bbox);
+  expand_rect (&tile_col->bbox, &bbox);
+  expand_rect (&row->bbox, &bbox);
+  expand_rect (&col->bbox, &bbox);
 
   self->dirty = TRUE;
   return marker;
@@ -357,8 +404,8 @@ shumate_vector_collision_remove (ShumateVectorCollision       *self,
   RTreeTileCol *tile_col;
   RTreeRow *row;
   RTreeCol *col;
-  gint64 tile_x = floor (marker->center.x / TILE_SIZE);
-  gint64 tile_y = floor (marker->center.y / TILE_SIZE);
+  gint64 tile_x = floor (marker->bbox.x / TILE_SIZE);
+  gint64 tile_y = floor (marker->bbox.y / TILE_SIZE);
 
   g_assert (self != NULL);
   g_assert (marker != NULL);
@@ -369,8 +416,8 @@ shumate_vector_collision_remove (ShumateVectorCollision       *self,
   tile_col = g_hash_table_lookup (tile_row->tile_cols, (gpointer) tile_x);
   g_assert (tile_col != NULL);
 
-  row = &tile_col->rows[row_for_position (marker->center.y)];
-  col = &row->cols[row_for_position (marker->center.x)];
+  row = &tile_col->rows[row_for_position (marker->bbox.y)];
+  col = &row->cols[row_for_position (marker->bbox.x)];
 
   self->markers = g_list_remove_link (self->markers, marker->list_link);
   g_list_free (marker->list_link);
@@ -395,7 +442,7 @@ detect_collision (ShumateVectorCollision       *self,
                   float                         rot,
                   float                         zoom)
 {
-  ShumateVectorCollisionRect bbox;
+  ShumateVectorCollisionBBox bbox;
 
   for (int z = 0; z < ZOOM_LAYERS; z ++)
     {
@@ -411,28 +458,28 @@ detect_collision (ShumateVectorCollision       *self,
           GHashTableIter tile_cols;
           RTreeTileCol *tile_col;
 
-          if (!rects_intersect (&bbox, &tile_row->rect))
+          if (!rects_intersect (&bbox, &tile_row->bbox))
             continue;
 
           g_hash_table_iter_init (&tile_cols, tile_row->tile_cols);
 
           while (g_hash_table_iter_next (&tile_cols, NULL, (gpointer*) &tile_col))
             {
-              if (!rects_intersect (&bbox, &tile_col->rect))
+              if (!rects_intersect (&bbox, &tile_col->bbox))
                 continue;
 
               for (int y = 0; y < NODES; y ++)
                 {
                   RTreeRow *row = &tile_col->rows[y];
 
-                  if (!rects_intersect (&bbox, &row->rect))
+                  if (!rects_intersect (&bbox, &row->bbox))
                     continue;
 
                   for (int x = 0; x < NODES; x ++)
                     {
                       RTreeCol *col = &row->cols[x];
 
-                      if (col->markers == NULL || !rects_intersect (&bbox, &col->rect))
+                      if (col->markers == NULL || !rects_intersect (&bbox, &col->bbox))
                         continue;
 
                       for (int i = 0; i < col->markers->len; i ++)
diff --git a/shumate/vector/shumate-vector-render-scope.c b/shumate/vector/shumate-vector-render-scope.c
index 3a156d6..8b9106b 100644
--- a/shumate/vector/shumate-vector-render-scope.c
+++ b/shumate/vector/shumate-vector-render-scope.c
@@ -197,6 +197,11 @@ shumate_vector_render_scope_get_bounds (ShumateVectorRenderScope *self,
           *max_y = MAX (*max_y, y);
         }
     }
+
+  *min_x /= self->layer->extent;
+  *min_y /= self->layer->extent;
+  *max_x /= self->layer->extent;
+  *max_y /= self->layer->extent;
 }
 
 
@@ -207,8 +212,8 @@ shumate_vector_render_scope_get_geometry_center (ShumateVectorRenderScope *self,
 {
   double min_x, min_y, max_x, max_y;
   shumate_vector_render_scope_get_bounds (self, &min_x, &min_y, &max_x, &max_y);
-  *x = (min_x + max_x) / 2.0 / self->layer->extent;
-  *y = (min_y + max_y) / 2.0 / self->layer->extent;
+  *x = (min_x + max_x) / 2.0;
+  *y = (min_y + max_y) / 2.0;
 }
 
 
diff --git a/shumate/vector/shumate-vector-symbol-container.c 
b/shumate/vector/shumate-vector-symbol-container.c
index c10aeb6..06625d8 100644
--- a/shumate/vector/shumate-vector-symbol-container.c
+++ b/shumate/vector/shumate-vector-symbol-container.c
@@ -301,22 +301,33 @@ shumate_vector_symbol_container_add_symbols (ShumateVectorSymbolContainer *self,
 
       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;
       info->y = symbol_info->y;
       info->tile_x = tile_x;
       info->tile_y = tile_y;
       info->zoom = zoom;
 
+      if (symbol_info->line_placement)
+        {
+          /* Use the line geometry and font size to get an upper bound on the
+           * symbol size */
+          info->width = symbol_info->line_size.x * 2 * tile_size + symbol_info->text_size;
+          info->height = symbol_info->line_size.y * 2 * tile_size + symbol_info->text_size;
+        }
+      else
+        {
+          /* Measure the label widget to get the symbol size */
+          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->marker = shumate_vector_collision_insert (self->collision,
                                                       zoom,
                                                       (tile_x + info->x) * tile_size,
                                                       (tile_y + info->y) * tile_size,
-                                                      -info->width / 2,
                                                       info->width / 2,
-                                                      -info->height / 2,
-                                                      info->height / 2);
+                                                      info->height / 2,
+                                                      !symbol_info->line_placement);
 
       self->children = g_list_prepend (self->children, info);
       gtk_widget_set_parent (GTK_WIDGET (info->symbol), GTK_WIDGET (self));
diff --git a/shumate/vector/shumate-vector-symbol-info-private.h 
b/shumate/vector/shumate-vector-symbol-info-private.h
index bfe19cd..09782b6 100644
--- a/shumate/vector/shumate-vector-symbol-info-private.h
+++ b/shumate/vector/shumate-vector-symbol-info-private.h
@@ -40,6 +40,7 @@ struct _ShumateVectorSymbolInfo
   double y;
 
   ShumateVectorLineString line;
+  ShumateVectorPoint line_size;
 
   /*< private >*/
   guint ref_count;
diff --git a/shumate/vector/shumate-vector-symbol-info.c b/shumate/vector/shumate-vector-symbol-info.c
index 8731b76..3dc2d97 100644
--- a/shumate/vector/shumate-vector-symbol-info.c
+++ b/shumate/vector/shumate-vector-symbol-info.c
@@ -89,6 +89,10 @@ void
 shumate_vector_symbol_info_set_line_points (ShumateVectorSymbolInfo *self,
                                             ShumateVectorLineString *linestring)
 {
+  ShumateVectorPoint center;
   shumate_vector_line_string_clear (&self->line);
   self->line = *linestring;
+  shumate_vector_line_string_bounds (&self->line, &self->line_size, &center);
+  self->x = center.x;
+  self->y = center.y;
 }
diff --git a/shumate/vector/shumate-vector-symbol-layer.c b/shumate/vector/shumate-vector-symbol-layer.c
index a73fba9..9ee597b 100644
--- a/shumate/vector/shumate-vector-symbol-layer.c
+++ b/shumate/vector/shumate-vector-symbol-layer.c
@@ -109,6 +109,7 @@ shumate_vector_symbol_layer_render (ShumateVectorLayer *layer, ShumateVectorRend
   double text_size;
   ShumateVectorSymbolInfo *symbol_info;
   double x, y;
+  double min_x, min_y, max_x, max_y;
 
   shumate_vector_render_scope_get_geometry_center (scope, &x, &y);
   if (x < 0 || x >= 1 || y < 0 || y >= 1)
@@ -116,6 +117,8 @@ shumate_vector_symbol_layer_render (ShumateVectorLayer *layer, ShumateVectorRend
      * covered by a different tile. */
     return;
 
+  shumate_vector_render_scope_get_bounds (scope, &min_x, &min_y, &max_x, &max_y);
+
   shumate_vector_expression_eval_color (self->text_color, scope, &text_color);
   text_size = shumate_vector_expression_eval_number (self->text_size, scope, 16.0);
   text_field = shumate_vector_expression_eval_string (self->text_field, scope, "");
diff --git a/shumate/vector/shumate-vector-symbol.c b/shumate/vector/shumate-vector-symbol.c
index 7ed9937..756c0c3 100644
--- a/shumate/vector/shumate-vector-symbol.c
+++ b/shumate/vector/shumate-vector-symbol.c
@@ -238,6 +238,10 @@ shumate_vector_symbol_snapshot (GtkWidget   *widget,
       if (self->glyphs_length > self->line_length * scale)
         return;
 
+      gtk_snapshot_save (snapshot);
+      gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (gtk_widget_get_allocated_width (widget) / 2,
+                                                              gtk_widget_get_allocated_height (widget) / 2));
+
       gtk_snapshot_rotate (snapshot, rotation * 180 / G_PI);
 
       shumate_vector_point_iter_init (&iter, &self->symbol_info->line);
@@ -268,6 +272,8 @@ shumate_vector_symbol_snapshot (GtkWidget   *widget,
 
           shumate_vector_point_iter_advance (&iter, glyph->width / scale);
         }
+
+      gtk_snapshot_restore (snapshot);
     }
   else
     GTK_WIDGET_CLASS (shumate_vector_symbol_parent_class)->snapshot (widget, snapshot);
diff --git a/shumate/vector/shumate-vector-utils-private.h b/shumate/vector/shumate-vector-utils-private.h
index 3c0d511..f424297 100644
--- a/shumate/vector/shumate-vector-utils-private.h
+++ b/shumate/vector/shumate-vector-utils-private.h
@@ -91,6 +91,8 @@ void   shumate_vector_point_iter_advance              (ShumateVectorPointIter *i
                                                        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);
+void   shumate_vector_line_string_bounds              (ShumateVectorLineString *linestring,
+                                                       ShumateVectorPoint      *radius_out,
+                                                       ShumateVectorPoint      *center_out);
diff --git a/shumate/vector/shumate-vector-utils.c b/shumate/vector/shumate-vector-utils.c
index 06dcf73..d2cf7c9 100644
--- a/shumate/vector/shumate-vector-utils.c
+++ b/shumate/vector/shumate-vector-utils.c
@@ -313,3 +313,31 @@ shumate_vector_line_string_length (ShumateVectorLineString *linestring)
 
   return sum;
 }
+
+
+void
+shumate_vector_line_string_bounds (ShumateVectorLineString *linestring,
+                                   ShumateVectorPoint      *radius_out,
+                                   ShumateVectorPoint      *center_out)
+{
+  guint i;
+  float min_x, max_x, min_y, max_y;
+
+  g_return_if_fail (linestring->n_points > 0);
+
+  min_x = max_x = linestring->points[0].x;
+  min_y = max_y = linestring->points[0].y;
+
+  for (i = 1; i < linestring->n_points; i ++)
+    {
+      min_x = MIN (min_x, linestring->points[i].x);
+      max_x = MAX (max_x, linestring->points[i].x);
+      min_y = MIN (min_y, linestring->points[i].y);
+      max_y = MAX (max_y, linestring->points[i].y);
+    }
+
+  radius_out->x = (max_x - min_x) / 2.0;
+  radius_out->y = (max_y - min_y) / 2.0;
+  center_out->x = (max_x + min_x) / 2.0;
+  center_out->y = (max_y + min_y) / 2.0;
+}
diff --git a/tests/vector-collision.c b/tests/vector-collision.c
index 21ac14b..7f333e8 100644
--- a/tests/vector-collision.c
+++ b/tests/vector-collision.c
@@ -11,20 +11,19 @@ test_vector_collision_nonoverlapping (void)
 
   collision = shumate_vector_collision_new ();
 
-  shumate_vector_collision_insert (collision, 0, 0, 0, -1, 1, -1, 1);
+  shumate_vector_collision_insert (collision, 0, 0, 0, -1, 1, TRUE);
 
   /* Far away markers */
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 10, 10, -1, 1, -1, 1));
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 100000, 0, -1, 1, -1, 
1));
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, 100000, -1, 1, -1, 
1));
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 100000, 100000, -1, 1, 
-1, 1));
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, 0, 100, 200, 100, 
200));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 10, 10, 1, 1, TRUE));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 100000, 0, 1, 1, TRUE));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, 100000, 1, 1, TRUE));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 100000, 100000, 1, 1, 
TRUE));
 
   /* Edge only */
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 2, 0, -1, 1, -1, 1));
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, -2, 0, -1, 1, -1, 1));
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, 2, -1, 1, -1, 1));
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, -2, -1, 1, -1, 1));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 2, 0, 1, 1, TRUE));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, -2, 0, 1, 1, TRUE));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, 2, 1, 1, TRUE));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, -2, 1, 1, TRUE));
 
   shumate_vector_collision_recalc (collision, 0, 0);
 
@@ -44,13 +43,13 @@ test_vector_collision_overlapping (void)
 
   collision = shumate_vector_collision_new ();
 
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, 0, -1, 1, -1, 1));
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, 0, -2, 2, -2, 2));
-  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 1, 1, -1, 1, -1, 1));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, 0, 1, 1, TRUE));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 0, 0, 2, 2, TRUE));
+  markers = g_list_prepend (markers, shumate_vector_collision_insert (collision, 0, 1, 1, 1, 1, TRUE));
 
   /* The current implementation uses g_list_prepend, so the last inserted
    * marker has priority. */
-  visible_marker = shumate_vector_collision_insert (collision, 0, 0, 0, -1, 1, -1, 1);
+  visible_marker = shumate_vector_collision_insert (collision, 0, 0, 0, 1, 1, TRUE);
   markers = g_list_prepend (markers, visible_marker);
 
   shumate_vector_collision_recalc (collision, 0, 0);
@@ -75,8 +74,8 @@ test_vector_collision_zoom (void)
 
   collision = shumate_vector_collision_new ();
 
-  marker1 = shumate_vector_collision_insert (collision, 1, 0, 0, -1, 1, -1, 1);
-  marker2 = shumate_vector_collision_insert (collision, 2, 3, 3, -2, 2, -2, 2);
+  marker1 = shumate_vector_collision_insert (collision, 1, 0, 0, 1, 1, TRUE);
+  marker2 = shumate_vector_collision_insert (collision, 2, 2, 2, 1, 1, TRUE);
 
   shumate_vector_collision_recalc (collision, 0, 1);
   g_assert_false (marker1->visible);
@@ -102,8 +101,8 @@ test_vector_collision_rotate (void)
 
   collision = shumate_vector_collision_new ();
 
-  marker1 = shumate_vector_collision_insert (collision, 0, 0, 0, -10, 10, -1, 1);
-  marker2 = shumate_vector_collision_insert (collision, 0, 0, 3, -10, 10, -1, 1);
+  marker1 = shumate_vector_collision_insert (collision, 0, 0, 0, 10, 1, TRUE);
+  marker2 = shumate_vector_collision_insert (collision, 0, 0, 3, 10, 1, TRUE);
 
   shumate_vector_collision_recalc (collision, 0, 0);
   g_assert_true (marker1->visible);


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