Complete.Org: Mailing Lists: Archives: freeciv-dev: October 2003:
[Freeciv-Dev] (PR#6439) get_mapview_scroll_window for iso-maps
Home

[Freeciv-Dev] (PR#6439) get_mapview_scroll_window for iso-maps

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#6439) get_mapview_scroll_window for iso-maps
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 8 Oct 2003 09:09:30 -0700
Reply-to: rt@xxxxxxxxxxxxxx

The current implementation of get_mapview_scroll_window only works for 
non-iso maps that wrap in the X direction only.


Well, it doesn't really work for that.  The non-iso-view implementation 
is almost correct:

   xmin = 0
   xmax = map.xsize
   xsize = tile_width

   ymin = 0
   ymax = map.ysize + 1
   ysize = tile_height

except that this doesn't allow scrolling to some legitimate positions on 
the X axis, nor does it allow convenient wrapping using the scroll bars. 
  If we were to change this to:

   xmin = -1
   xsize = tile_width
   xmax = map.xsize + xsize

then both issues would be fixed.


The iso-view implementation is even more problematic.  For one thing, 
there is the same wrapping issue as in non-iso view.  For another, the 
handling of EXTRA_BOTTOM_ROW is fallacious:

   ymin = 0
   ysize = tile_height
   ymax = map.ysize + 6

which is theoretically flawed.  The minimum Y value isn't at the 
top-left corner of the mapview (the origin off of which scroll positions 
are based), it's at the top-right corner.  This is an offset of 
tile_width.  Setting ymin to 0 just happens to get things right because 
it means the northern border of the map will run straight through the 
origin.  The value of tile_height is also wrong, since the slider moves 
through map/native coordinates rather than gui-aligned ones.  And the 
ymax value is wrong, since it also ignores the offset from the minimum - 
but this happens to work out decently since the ysize value is too small 
anyway.  The result is that you can see the whole map, but the number of 
unreal positions you're shown at the top and bottom of the map are 
different and may vary depending on the size of the map.

These values should become

   ymin = tile_width - 6
   ysize = xsize = tile_width + tile_height
   ymax = map.ysize + tile_width + 6

to fix these problems.  Note that this gives 6 extra tiles at top and 
bottom of the map.  It's probably better to make this value proportional 
to the size of the window; say MAX(tile_width,tile_height)/2 - imagine 
if the map window were only 6 tiles across.

The attached patch easy-scroll.diff makes these changes.  Notice how the 
borders are now symmetric in iso-view, and scrolling along the X axis 
can be done fully in both views.

-----

All of this is an introduction to the second patch, which makes all of 
the above corrections but works for all topology conditions.  It is 
intended to be self-documenting, so I won't say anything more about it here.

I can't guarantee it doesn't have bugs, since there are 16 different 
topology/GUI scenarios and the size of the mapview window plays a role 
in behavior as well.  But I do believe it's pretty good.

jason

Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.66
diff -u -r1.66 mapview_common.c
--- client/mapview_common.c     2003/10/06 11:46:27     1.66
+++ client/mapview_common.c     2003/10/08 15:56:45
@@ -355,11 +355,24 @@
 void get_mapview_scroll_window(int *xmin, int *ymin, int *xmax, int *ymax,
                               int *xsize, int *ysize)
 {
-  *xmin = *ymin = 0;
-  *xmax = map.xsize;
-  *ymax = map.ysize + EXTRA_BOTTOM_ROW;
-  *xsize = mapview_canvas.tile_width;
-  *ysize = mapview_canvas.tile_height;
+  if (is_isometric) {
+    *xsize = mapview_canvas.tile_height + mapview_canvas.tile_width;
+    *ysize = *xsize;
+  } else {
+    *xsize = mapview_canvas.tile_width;
+    *ysize = mapview_canvas.tile_height;
+  }
+
+  *xmin = -1;
+  *xmax = map.xsize + *xsize;
+
+  if (is_isometric) {
+    *ymin = mapview_canvas.tile_width - 6;
+    *ymax = map.ysize + mapview_canvas.tile_width + 6;
+  } else {
+    *ymin = 0;
+    *ymax = map.ysize + 1;
+  }
 }
 
 /****************************************************************************
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.66
diff -u -r1.66 mapview_common.c
--- client/mapview_common.c     2003/10/06 11:46:27     1.66
+++ client/mapview_common.c     2003/10/08 15:42:45
@@ -349,17 +349,132 @@
 }
 
 /****************************************************************************
-  Return the range of values that the mapview origin can take, in scroll
-  positions.  Useful for scrollbars or when manually clipping the window.
+  Return the scroll dimensions of the clipping window for the mapview window..
+
+  Imagine the entire map in scroll coordinates.  It is a rectangle.  Now
+  imagine the mapview "window" sliding around through this rectangle.  How
+  far can it slide?  In most cases it has to be able to slide past the
+  ends of the map rectangle so that it's capable of reaching the whole
+  area.
+
+  This function gives constraints on how far the window is allowed to
+  slide.  xmin and ymin are the minimum values for the window origin.
+  xsize and ysize give the scroll dimensions of the mapview window.
+  xmax and ymax give the maximum values that the bottom/left ends of the
+  window may reach.  The constraints, therefore, are that:
+
+    get_mapview_scroll_pos(&scroll_x, &scroll_y);
+    xmin <= scroll_x < xmax - xsize
+    ymin <= scroll_y < ymax - ysize
+
+  This function should be used anywhere and everywhere that scrolling is
+  constrained.
+
+  Note that scroll coordinates, not map coordinates, are used.  Currently
+  these correspond to native coordinates.
 ****************************************************************************/
 void get_mapview_scroll_window(int *xmin, int *ymin, int *xmax, int *ymax,
                               int *xsize, int *ysize)
 {
-  *xmin = *ymin = 0;
+  /* There are a number of factors that must be taken into account in
+   * calculating these values:
+   *
+   * 1. Basic constraints: X should generally range from 0 to map.xsize;
+   * Y from 0 to map.ysize.
+   *
+   * 2. Non-aligned borders: if the borders don't line up (an iso-view client
+   * with a standard map, for instance) the minimum and maximum must be
+   * extended if the map doesn't wrap in that direction.  They are extended
+   * by an amount proportional to the size of the screen.
+   *
+   * 3. Compression: on an iso-map native coordinates are compressed 2x in
+   * the X direction.
+   *
+   * 4. Translation: the min and max values give a range for the origin.
+   * Since the base constraint is on the minimal value contained in the
+   * mapview, we have to translate the minimum and maximum to account for
+   * this.
+   *
+   * 5. Wrapping: if the map wraps in a given direction, no border adjustment
+   * or translation is needed.  Instead we have to make sure the range is
+   * large enough to get the full wrap.
+   */
+  *xmin = 0;
+  *ymin = 0;
   *xmax = map.xsize;
-  *ymax = map.ysize + EXTRA_BOTTOM_ROW;
-  *xsize = mapview_canvas.tile_width;
-  *ysize = mapview_canvas.tile_height;
+  *ymax = map.ysize;
+
+  if (topo_has_flag(TF_ISO) != is_isometric) {
+    /* The mapview window is aligned differently than the map.  In this
+     * case we need to give looser constraints because (if the map doesn't
+     * wrap) the edges will not line up well. */
+
+    /* These are the dimensions of the bounding box. */
+    *xsize = *ysize =
+      mapview_canvas.tile_width + mapview_canvas.tile_height;
+
+    if (is_isometric) {
+      /* Step 2: Calculate extra border distance. */
+      *xmin = *ymin = -(MAX(mapview_canvas.tile_width,
+                           mapview_canvas.tile_height) + 1) / 2;
+      *xmax -= *xmin;
+      *ymax -= *ymin;
+
+      /* Step 4: Translate the Y coordinate.  The mapview origin is at the
+       * top-left corner of the window, which is offset at +tile_width from
+       * the minimum Y value (at the top-right corner). */
+      *ymin += mapview_canvas.tile_width;
+      *ymax += mapview_canvas.tile_width;
+    } else {
+      /* Compression. */
+      *xsize = (*xsize + 1) / 2;
+
+      /* Step 2: calculate border adjustment. */
+      *ymin = -(MAX(mapview_canvas.tile_width,
+                   mapview_canvas.tile_height) + 1) / 2;
+      *xmin = (*ymin + 1) / 2; /* again compressed */
+      *xmax -= *xmin;
+      *ymax -= *ymin;
+
+      /* Step 4: translate the X coordinate.  The mapview origin is at the
+       * top-left corner of the window, which is offset at +tile_height/2
+       * from the minimum X value (at the bottom-left corner). */
+      *xmin += mapview_canvas.tile_height / 2;
+      *xmax += (mapview_canvas.tile_height + 1) / 2;
+    }
+  } else {
+    *xsize = mapview_canvas.tile_width;
+    *ysize = mapview_canvas.tile_height;
+
+    if (is_isometric) {
+      /* Compression: Each vertical half-tile corresponds to one native
+       * unit (a full horizontal tile corresponds to a native unit). */
+      *ysize = (mapview_canvas.height - 1) / (NORMAL_TILE_HEIGHT / 2) + 1;
+
+      /* Isometric fixes: the above calculations are in half-tiles; since we
+       * need to see full tiles we have to extend the range a bit.  This also
+       * corrects for the off-by-one error caused by the X compression of
+       * native coordinates. */
+      (*xmin)--;
+      (*xmax)++;
+      (*ymax) += 2;
+    }
+  }
+
+  /* Now override the above to satisfy wrapping constraints.  We allow the
+   * scrolling to cover the full range of the map, plus one unit in each
+   * direction (to allow scrolling with the scroll bars, for instance). */
+  if (topo_has_flag(TF_WRAPX)) {
+    *xmin = -1;
+    *xmax = map.xsize + *xsize;
+  }
+  if (topo_has_flag(TF_WRAPY)) {
+    *ymin = -1;
+    *ymax = map.ysize + *ysize;
+  }
+
+  freelog(LOG_DEBUG, "x: %d<-%d->%d; y: %d<-%d->%d",
+         *xmin, *xsize, *xmax, *ymin, *ymax, *ysize);
 }
 
 /****************************************************************************

[Prev in Thread] Current Thread [Next in Thread]