Complete.Org: Mailing Lists: Archives: freeciv-dev: March 2004:
[Freeciv-Dev] (PR#8299) wrapping GUI coordinates
Home

[Freeciv-Dev] (PR#8299) wrapping GUI coordinates

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#8299) wrapping GUI coordinates
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Mon, 22 Mar 2004 10:56:57 -0800
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=8299 >

GUI coordinates need to wrap.  That way when you scroll across using the 
scrollbar you'll get the expected results.

There are two possible ways to do this: the easy way, and the hard way.

In the easy way we convert the GUI position to a map position, wrap that 
map position, then convert it back to a GUI position.  We have to 
remember the GUI position's offset from the map position's tile so that 
we end up with the proper result.  Attached patch "A" does this.  The 
advantage is that it is easy.  The disadvantage is that it's a hack and 
won't be very extensible to other problems.

In the hard way we convert the GUI position to a "native" gui position. 
  The native position is aligned natively to the freeciv map, but 
preserves pixel resolution so that each tile is of size 
NORMAL_TILE_WIDHT*NORMAL_TILE_HEIGHT.  We can then wrap this position 
directly before converting it back to a gui position.  Attached patch 
"B" does this.  The disadvantage is that this is hard: it takes more 
code than the above method, the math is hard to follow, and the very 
concept of "gui-native" coordinates is a bit bizarre.  The advantage is 
that it is a more flexible design: gui-native coordinates can be used to 
do just about any topological operation needed on GUI coordinates while 
preserving pixel resolution (for instance a gui_distance_vector() 
function would be just a few lines).

I'm not sure which method is better.

jason

Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.94
diff -u -r1.94 mapview_common.c
--- client/mapview_common.c     19 Mar 2004 20:22:07 -0000      1.94
+++ client/mapview_common.c     22 Mar 2004 18:05:09 -0000
@@ -324,12 +324,43 @@
 }
 
 /****************************************************************************
+  Normalize (wrap) the GUI position.  This is equivalent to a map wrapping,
+  but in GUI coordinates so that pixel accuracy is preserved.
+****************************************************************************/
+static void normalize_gui_pos(int *gui_x, int *gui_y)
+{
+  int map_x, map_y, nat_x, nat_y, gui_x0, gui_y0, diff_x, diff_y;
+
+  gui_to_map_pos(&map_x, &map_y, *gui_x, *gui_y);
+  map_to_gui_pos(&gui_x0, &gui_y0, map_x, map_y);
+  diff_x = *gui_x - gui_x0;
+  diff_y = *gui_y - gui_y0;
+
+  /* Perform wrapping without any realness check. */
+  map_to_native_pos(&nat_x, &nat_y, map_x, map_y);
+  if (topo_has_flag(TF_WRAPX)) {
+    nat_x = FC_WRAP(nat_x, map.xsize);
+  }
+  if (topo_has_flag(TF_WRAPY)) {
+    nat_y = FC_WRAP(nat_y, map.ysize);
+  }
+  native_to_map_pos(&map_x, &map_y, nat_x, nat_y);
+
+  map_to_gui_pos(gui_x, gui_y, map_x, map_y);
+  *gui_x += diff_x;
+  *gui_y += diff_y;
+}
+
+/****************************************************************************
   Change the mapview origin, clip it, and update everything.
 ****************************************************************************/
 static void set_mapview_origin(int gui_x0, int gui_y0)
 {
   int xmin, xmax, ymin, ymax, xsize, ysize;
 
+  /* Normalize (wrap) the mapview origin. */
+  normalize_gui_pos(&gui_x0, &gui_y0);
+
   /* First wrap/clip the position.  Wrapping is done in native positions
    * while clipping is done in scroll (native) positions. */
   get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
@@ -404,12 +435,24 @@
     *ymax += NORMAL_TILE_HEIGHT;
 
     /* To be able to center on positions near the edges, we have to be
-     * allowed to scroll past those edges. */
+     * allowed to scroll all the way to those edges.  To allow wrapping the
+     * clipping boundary needs to extend past the edge - a half-tile in
+     * iso-view or a full tile in non-iso view.  The above math already has
+     * taken care of some of this so all that's left is to fix the corner
+     * cases. */
     if (topo_has_flag(TF_WRAPX)) {
       *xmax += *xsize;
+      if (!is_isometric) {
+       /* In non-iso view we need to be able to scroll a little further to
+        * the left. */
+       *xmin -= NORMAL_TILE_WIDTH;
+      }
     }
     if (topo_has_flag(TF_WRAPY)) {
       *ymax += *ysize;
+      if (!is_isometric) {
+       *ymin -= NORMAL_TILE_HEIGHT;
+      }
     }
   } else {
     /* Otherwise it's hard.  Very hard.  Impossible, in fact.  This is just
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.94
diff -u -r1.94 mapview_common.c
--- client/mapview_common.c     19 Mar 2004 20:22:07 -0000      1.94
+++ client/mapview_common.c     22 Mar 2004 18:44:20 -0000
@@ -324,12 +324,104 @@
 }
 
 /****************************************************************************
+  A "gui" "native" coordinate system is oriented the same way as the map,
+  but preserves pixel granularity for the canvas. Each tile is of size
+  NORMAL_TILE_WIDTH * NORMAL_TILE_HEIGHT in each direction.
+****************************************************************************/
+static void gui_to_guinat_pos(int *guinat_x, int *guinat_y,
+                             int gui_x, int gui_y)
+{
+  const int W = NORMAL_TILE_WIDTH, H = NORMAL_TILE_HEIGHT;
+  int guimap_x, guimap_y;
+
+  /* This echoes the conversion in gui_to_map_pos, except we don't lose
+   * pixel granularity.  We transorm GUI tiles into square tiles with
+   * side W * H. */
+  if (is_isometric) {
+    guimap_x = gui_x * H + gui_y * W;
+    guimap_y = gui_y * W - gui_x * H;
+  } else {
+    guimap_x = gui_x * H;
+    guimap_y = gui_y * W;
+  }
+
+  /* Now convert into a "gui" native position.  This echoes the conversion
+   * in map_to_native_pos, except again we keep pixel granularity. */
+  if (topo_has_flag(TF_ISO)) {
+    *guinat_y = guimap_x + guimap_y - map.xsize * W * H;
+    *guinat_x = (2 * guimap_x - *guinat_y - (*guinat_y & 1)) / 2;
+  } else {
+    *guinat_x = guimap_x;
+    *guinat_y = guimap_y;
+  }
+}
+
+/****************************************************************************
+  A "gui" "native" coordinate system is oriented the same way as the map,
+  but preserves pixel granularity for the canvas. Each tile is of size
+  NORMAL_TILE_WIDTH * NORMAL_TILE_HEIGHT in each direction.
+****************************************************************************/
+static void guinat_to_gui_pos(int *gui_x, int *gui_y,
+                             int guinat_x, int guinat_y)
+{
+  const int W = NORMAL_TILE_WIDTH, H = NORMAL_TILE_HEIGHT;
+  int guimap_x, guimap_y;
+
+  /* Convert the "gui-native" position into a "gui-map" position.  This
+   * echoes the conversion in native_to_map_pos, except the scale is
+   * larger. */;
+  if (topo_has_flag(TF_ISO)) {
+    guimap_x = (guinat_y + (guinat_y & 1)) / 2 + guinat_x;
+    guimap_y = guinat_y - guimap_x + map.xsize * W * H;
+  } else {
+    guimap_x = guinat_x;
+    guimap_y = guinat_y;
+  }
+
+  /* Now convert directly into a GUI position.  This echoes the conversion
+   * in map_to_gui_pos, except the scale is larger. */
+  if (is_isometric) {
+    *gui_x = (guimap_x - guimap_y) / (H * 2);
+    *gui_y = (guimap_x + guimap_y) / (W * 2);
+  } else {
+    *gui_x = guimap_x / H;
+    *gui_y = guimap_y / W;
+  }
+}
+
+/****************************************************************************
+  Normalize (wrap) the GUI position.  This is equivalent to a map wrapping,
+  but in GUI coordinates so that pixel accuracy is preserved.
+****************************************************************************/
+static void normalize_gui_pos(int *gui_x, int *gui_y)
+{
+  const int W = NORMAL_TILE_WIDTH, H = NORMAL_TILE_HEIGHT;
+  int guinat_x, guinat_y;
+
+  gui_to_guinat_pos(&guinat_x, &guinat_y, *gui_x, *gui_y);
+
+  /* Now wrap. */
+  if (topo_has_flag(TF_WRAPX)) {
+    guinat_x = FC_WRAP(guinat_x, map.xsize * W * H);
+  }
+  if (topo_has_flag(TF_WRAPY)) {
+    guinat_y = FC_WRAP(guinat_y, map.ysize * W * H);
+  }
+
+  /* Convert back to "gui" map position. */
+  guinat_to_gui_pos(gui_x, gui_y, guinat_x, guinat_y);
+}
+
+/****************************************************************************
   Change the mapview origin, clip it, and update everything.
 ****************************************************************************/
 static void set_mapview_origin(int gui_x0, int gui_y0)
 {
   int xmin, xmax, ymin, ymax, xsize, ysize;
 
+  /* Normalize (wrap) the mapview origin. */
+  normalize_gui_pos(&gui_x0, &gui_y0);
+
   /* First wrap/clip the position.  Wrapping is done in native positions
    * while clipping is done in scroll (native) positions. */
   get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
@@ -404,12 +496,20 @@
     *ymax += NORMAL_TILE_HEIGHT;
 
     /* To be able to center on positions near the edges, we have to be
-     * allowed to scroll past those edges. */
+     * allowed to scroll all the way to those edges.  To allow wrapping the
+     * clipping boundary needs to extend past the edge - a half-tile in
+     * iso-view or a full tile in non-iso view.  The above math already has
+     * taken care of some of this so all that's left is to fix the corner
+     * cases. */
     if (topo_has_flag(TF_WRAPX)) {
       *xmax += *xsize;
+
+      /* We need to be able to scroll a little further to the left. */
+      *xmin -= NORMAL_TILE_WIDTH;
     }
     if (topo_has_flag(TF_WRAPY)) {
       *ymax += *ysize;
+      *ymin -= NORMAL_TILE_HEIGHT;
     }
   } else {
     /* Otherwise it's hard.  Very hard.  Impossible, in fact.  This is just

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#8299) wrapping GUI coordinates, Jason Short <=