Complete.Org: Mailing Lists: Archives: freeciv-dev: January 2002:
[Freeciv-Dev] demo: rotating the map view
Home

[Freeciv-Dev] demo: rotating the map view

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: freeciv-dev <freeciv-dev@xxxxxxxxxxx>
Subject: [Freeciv-Dev] demo: rotating the map view
From: Jason Short <vze2zq63@xxxxxxxxxxx>
Date: Mon, 14 Jan 2002 14:23:33 -0500
Reply-to: jdorje@xxxxxxxxxxxx

I had this idea the other day, and just couldn't rest until I had a working prototype :-). It allows the mapview to be rotated in 90 degree (pi/2) increments.

Don't bother looking at the code - it basically consisted of applying my find_representative_map_pos and canvas_to_map_pos patches, changing canvas_to_map_pos and map_to_canvas_pos, and hacking about a dozen places to correctly convert directions from "gui" to "map" directions. But there's no layering or clean implementation.

So basically - just apply it and try out the flat (overhead) view with gui-gtk. From the view menu there's a "rotate view" selection. A little bit of the drawing code hasn't been dealt with, so some of the pictures (like blinking units (!)) aren't drawn correctly.

As for the parts that work, well, I don't think the average user would not really use a rotated view much or appreciate how difficult it was to get it to work (the math _really_ hurt my head; it was late at night :-). But the concepts it requires (separation of gui and map) are generally useful for combining iso- and flat- view code and for implementing topologies that aren't well oriented (like mobius strips), so it is of pretty high theoretical interest.

Well, I'm tired of explaining...eventually I'll probably clean this code up and make a clean interface for converting the vectors. Until then, enjoy.

Oh, a final note: only the mapview is rotated, not the cityview. The overview is updated to reflect the changed shape of the mapview, but does not give the orientation.

jason
? diff
? jason-game-2.gz
? jason-game.gz
? jason.gz
? jasongame-test.gz
? my-freeciv-patches
? old
? rc
? test.pl
? topology
? data/lexxy
? data/lexxy.tilespec
Index: client/citydlg_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/citydlg_common.c,v
retrieving revision 1.3
diff -u -r1.3 citydlg_common.c
--- client/citydlg_common.c     2001/12/13 15:30:30     1.3
+++ client/citydlg_common.c     2002/01/14 11:48:23
@@ -50,33 +50,36 @@
 **************************************************************************/
 void canvas_pos_to_city_pos(int canvas_x, int canvas_y, int *map_x, int *map_y)
 {
+  int orig_canvas_x = canvas_x, orig_canvas_y = canvas_y;
   if (is_isometric) {
-    *map_x = -2;
-    *map_y = 2;
+    /*
+     * The top-left corner is in the center of tile (-2, 2).  The math used
+     * here is identical to that used in canvas_pos_to_map_pos; see that
+     * function for a full explanation.
+     */
+    int flat_canvas_x, flat_canvas_y;
 
-    /* first find an equivalent position on the left side of the screen. */
-    *map_x += canvas_x / NORMAL_TILE_WIDTH;
-    *map_y -= canvas_x / NORMAL_TILE_WIDTH;
-    canvas_x %= NORMAL_TILE_WIDTH;
+    /* Shift the tile right so the top corner of tile (-2,-2) is at (0,0). */
+    canvas_y += NORMAL_TILE_HEIGHT / 2;
 
-    /* Then move op to the top corner. */
-    *map_x += canvas_y / NORMAL_TILE_HEIGHT;
-    *map_y += canvas_y / NORMAL_TILE_HEIGHT;
-    canvas_y %= NORMAL_TILE_HEIGHT;
+    /* First we scale it to a square. */
+    canvas_x *= NORMAL_TILE_HEIGHT;
+    canvas_y *= NORMAL_TILE_WIDTH;
 
-    assert(NORMAL_TILE_WIDTH == 2 * NORMAL_TILE_HEIGHT);
-    canvas_y *= 2;             /* now we have a square. */
-    if (canvas_x + canvas_y > NORMAL_TILE_WIDTH / 2)
-      (*map_x)++;
-    if (canvas_x + canvas_y > 3 * NORMAL_TILE_WIDTH / 2)
-      (*map_x)++;
-    if (canvas_x - canvas_y > NORMAL_TILE_WIDTH / 2)
-      (*map_y)--;
-    if (canvas_y - canvas_x > NORMAL_TILE_WIDTH / 2)
-      (*map_y)++;
+    /* Then we transform it to make it flat. */
+    flat_canvas_x = canvas_x + canvas_y;
+    flat_canvas_y = canvas_y - canvas_x;
+
+    /* Now we've got a square, so it's a simple matter to find the
+       position. */
+    *map_x = -2 + DIVIDE(flat_canvas_x,
+                        NORMAL_TILE_WIDTH * NORMAL_TILE_HEIGHT);
+    *map_y = 2 + DIVIDE(flat_canvas_y,
+                       NORMAL_TILE_WIDTH * NORMAL_TILE_HEIGHT);
   } else {
     *map_x = canvas_x / NORMAL_TILE_WIDTH;
     *map_y = canvas_y / NORMAL_TILE_HEIGHT;
   }
-  freelog(LOG_DEBUG, "canvas_pos_to_city_pos(pos=(%d,%d))=(%d,%d)", canvas_x, 
canvas_y, *map_x, *map_y);
+  freelog(LOG_DEBUG, "canvas_pos_to_city_pos(pos=(%d,%d))=(%d,%d)",
+         orig_canvas_x, orig_canvas_y, *map_x, *map_y);
 }
Index: client/control.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/control.c,v
retrieving revision 1.64
diff -u -r1.64 control.c
--- client/control.c    2001/12/08 15:15:50     1.64
+++ client/control.c    2002/01/14 11:48:24
@@ -598,6 +598,8 @@
   int dest_x, dest_y;
   struct unit req_unit;
 
+  dir = rotate_dir_ccw(mapview_orientation * 2, dir);
+
   /* Catches attempts to move off map */
   if (!MAPSTEP(dest_x, dest_y, punit->x, punit->y, dir)) {
     return;
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.4
diff -u -r1.4 mapview_common.c
--- client/mapview_common.c     2001/12/21 16:53:10     1.4
+++ client/mapview_common.c     2002/01/14 11:48:25
@@ -81,7 +81,31 @@
   }
 }
 
+int mapview_orientation = 0;
+
 /**************************************************************************
+  Changes the orientation of the mapview to match the given new
+  orientation.
+**************************************************************************/
+void set_mapview_orientation(int new_orientation)
+{
+  if (is_isometric) {
+    return; /* I don't think so! */
+  }
+  assert(new_orientation >= 0 && new_orientation < 4);
+  mapview_orientation = new_orientation;
+  /* FIXME: update graphics */
+  update_map_canvas_visible();
+}
+
+#define SWAP(a, b) \
+do {               \
+  int _c = a;      \
+  a = b;           \
+  b = _c;          \
+} while(0)
+
+/**************************************************************************
   Finds the pixel coordinates of a tile.  Beside setting the results
   in canvas_x,canvas_y it returns whether the tile is inside the
   visible map.
@@ -93,27 +117,54 @@
                          int map_view_pixel_width,
                          int map_view_pixel_height)
 {
+  struct win_map_context win_mc;
+
+  /*
+   * First we wrap the coordinates to hopefully be within the the
+   * GUI window.  We leave the actual wrapping up to the
+   * find_representative_map_pos function, but we must give it the
+   * location of the GUI window.
+   */
+  win_mc.win_is_iso = is_isometric;
+  win_mc.x0 = map_view_topleft_map_x;
+  win_mc.y0 = map_view_topleft_map_y;
   if (is_isometric) {
+    /* We must round up; plus we get an extra tile on all four sides. */
+    win_mc.width = (map_view_pixel_width - 1) / (NORMAL_TILE_WIDTH/2) + 3;
+    win_mc.height = (map_view_pixel_height - 1) / (NORMAL_TILE_HEIGHT/2) + 3;
+  } else {
+    win_mc.width = (map_view_pixel_width - 1) / NORMAL_TILE_WIDTH + 1;
+    win_mc.height = (map_view_pixel_height - 1) / NORMAL_TILE_HEIGHT + 1;
+  }
+  switch (mapview_orientation) {
+    /* Only works for flat-view!!! */
+    case 0:
+      break;
+    case 1:
+      SWAP(win_mc.width, win_mc.height);
+      break;
+    case 2:
+      win_mc.x0 -= win_mc.width;
+      win_mc.y0 -= win_mc.height;
+      break;
+    case 3:
+      SWAP(win_mc.width, win_mc.height);
+      break;
+  }
+
+  find_representative_map_pos(&map_x, &map_y, &win_mc);
+
+  /* Next, we find the canvas _center_ of the tile in question. */
+  if (is_isometric) {
     /* For a simpler example of this math, see
        city_pos_to_canvas_pos(). */
     int iso_x, iso_y;
 
     /*
-     * First we wrap the coordinates to hopefully be within the the
-     * GUI window.  This isn't perfect; notice that when the mapview
-     * approaches the size of the map some tiles won't be shown at
-     * all.
-     */
-    map_x %= map.xsize;
-    if (map_x < map_view_topleft_map_x) {
-      map_x += map.xsize;
-    }
-
-    /*
      * Next we convert the flat GUI coordinates to isometric GUI
      * coordinates.  We'll make tile (x0, y0) be the origin, and
      * transform like this:
-     * 
+     *
      *                     3
      * 123                2 6
      * 456 -> becomes -> 1 5 9
@@ -130,49 +181,45 @@
     /*
      * As the above picture shows, each isometric-coordinate unit
      * corresponds to a half-tile on the canvas.  Since the (x0, y0)
-     * tile actually has its top corner (of the diamond-shaped tile)
-     * located right at the corner of the canvas, to find the top-left
-     * corner of the surrounding rectangle we must subtract off an
-     * additional half-tile in the X direction.
+     * tile actually has its center (of the diamond-shaped tile) right
+     * at canvas position (0,0), all we have to do is multiply to find
+     * the center.
      */
-    *canvas_x = (iso_x - 1) * NORMAL_TILE_WIDTH / 2;
-    *canvas_y = iso_y * NORMAL_TILE_HEIGHT / 2;
-
-    /*
-     * Finally we clip; checking to see if _any part_ of the tile is
-     * visible on the canvas.
-     */
-    return (*canvas_x > -NORMAL_TILE_WIDTH)
-       && *canvas_x < (map_view_pixel_width + NORMAL_TILE_WIDTH / 2)
-       && (*canvas_y > -NORMAL_TILE_HEIGHT)
-       && (*canvas_y < map_view_pixel_height);
+    assert(NORMAL_TILE_WIDTH % 2 == 0 && NORMAL_TILE_HEIGHT % 2 == 0);
+    *canvas_x = DIVIDE((iso_x - 1) * NORMAL_TILE_WIDTH, 2);
+    *canvas_y = DIVIDE((iso_y - 1) * NORMAL_TILE_HEIGHT, 2);
   } else {                     /* is_isometric */
-    /* map_view_map_width is the width in tiles/map positions */
-    int map_view_map_width =
-       (map_view_pixel_width + NORMAL_TILE_WIDTH - 1) / NORMAL_TILE_WIDTH;
-
-    if (map_view_topleft_map_x + map_view_map_width <= map.xsize) {
-      *canvas_x = map_x - map_view_topleft_map_x;
-    } else if (map_x >= map_view_topleft_map_x) {
-      *canvas_x = map_x - map_view_topleft_map_x;
+    switch (mapview_orientation) {
+      case 0:
+        *canvas_x = (map_x - map_view_topleft_map_x) * NORMAL_TILE_WIDTH;
+        *canvas_y = (map_y - map_view_topleft_map_y) * NORMAL_TILE_HEIGHT;
+        break;
+      case 1:
+        *canvas_x = map_view_pixel_width -
+               (map_y - map_view_topleft_map_y) * NORMAL_TILE_WIDTH;
+        *canvas_y = (map_x - map_view_topleft_map_x) * NORMAL_TILE_HEIGHT;
+        break;
+      case 2:
+        *canvas_x = map_view_pixel_width -
+               (map_x - map_view_topleft_map_x) * NORMAL_TILE_WIDTH;
+        *canvas_y = map_view_pixel_height -
+               (map_y - map_view_topleft_map_y) * NORMAL_TILE_HEIGHT;
+        break;
+      case 3:
+        *canvas_x = (map_y - map_view_topleft_map_y) * NORMAL_TILE_WIDTH;
+        *canvas_y = map_view_pixel_height -
+               (map_x - map_view_topleft_map_x) * NORMAL_TILE_HEIGHT;
     }
-      else if (map_x <
-              map_adjust_x(map_view_topleft_map_x +
-                           map_view_map_width)) {*canvas_x =
-         map_x + map.xsize - map_view_topleft_map_x;
-    } else {
-      *canvas_x = -1;
-    }
-
-    *canvas_y = map_y - map_view_topleft_map_y;
-
-    *canvas_x *= NORMAL_TILE_WIDTH;
-    *canvas_y *= NORMAL_TILE_HEIGHT;
-
-    return *canvas_x >= 0
-       && *canvas_x < map_view_pixel_width
-       && *canvas_y >= 0 && *canvas_y < map_view_pixel_height;
   }
+
+  /*
+   * Finally we clip; checking to see if _any part_ of the tile is
+   * visible on the canvas.
+   */
+  return  (*canvas_x > -NORMAL_TILE_WIDTH
+       && *canvas_x <= map_view_pixel_width
+       && *canvas_y > -NORMAL_TILE_HEIGHT
+       && *canvas_y <= map_view_pixel_height);
 }
 
 /**************************************************************************
@@ -181,38 +228,93 @@
 void canvas_pos_to_map_pos(int canvas_x, int canvas_y,
                           int *map_x, int *map_y,
                           int map_view_topleft_map_x,
-                          int map_view_topleft_map_y)
+                          int map_view_topleft_map_y,
+                          int map_view_pixel_width,
+                          int map_view_pixel_height)
 {
+  switch (mapview_orientation) {
+    case 0:
+      break;
+    case 1:
+      canvas_x = map_view_pixel_width - canvas_x;
+      SWAP(canvas_x, canvas_y);
+      break;
+    case 2:
+      canvas_x = map_view_pixel_width - canvas_x;
+      canvas_y = map_view_pixel_height - canvas_y;
+      break;
+    case 3:
+      canvas_y = map_view_pixel_height - canvas_y;
+      SWAP(canvas_x, canvas_y);
+      break;
+  }
+
   if (is_isometric) {
-    *map_x = map_view_topleft_map_x;
-    *map_y = map_view_topleft_map_y;
+    /* For another example of this math, see canvas_pos_to_city_pos(). */
+    int flat_canvas_x, flat_canvas_y;
+    int offset;
+
+    /* If the tile width and height are not equal, then it will take very
+     * ugly math to figure anything out about it.  The easiest solution is
+     * just to "scale" the tile so that it's a perfect (diamond) square.  We
+     * are doing the following transformation to a tile:
+     *
+     *                                 XX  <- height=18, width=18
+     *   XX   <- height=3, width=6
+     * XXXXXX                        XXXXXX
+     *   XX   -----> becomes ----->
+     *                                 XX
+     *
+     * Note that all we really need to do is scale to the LCM of the width
+     * and height (in the above example, 6x6).  But it's easier just to scale
+     * to WIDTH*HEIGHT (18x18).
+     */
+    canvas_x *= NORMAL_TILE_HEIGHT;
+    canvas_y *= NORMAL_TILE_WIDTH;
 
-    /* first find an equivalent position on the left side of the screen. */
-    *map_x += canvas_x / NORMAL_TILE_WIDTH;
-    *map_y -= canvas_x / NORMAL_TILE_WIDTH;
-    canvas_x %= NORMAL_TILE_WIDTH;
-
-    /* Then move op to the top corner. */
-    *map_x += canvas_y / NORMAL_TILE_HEIGHT;
-    *map_y += canvas_y / NORMAL_TILE_HEIGHT;
-    canvas_y %= NORMAL_TILE_HEIGHT;
-
-    /* We are inside a rectangle, with 2 half tiles starting in the
-       corner, and two tiles further out. Draw a grid to see how this
-       works :). */
-    assert(NORMAL_TILE_WIDTH == 2 * NORMAL_TILE_HEIGHT);
-    canvas_y *= 2;             /* now we have a square. */
-    if (canvas_x > canvas_y) {
-      *map_y -= 1;
-    }
-    if (canvas_x + canvas_y > NORMAL_TILE_WIDTH) {
-      *map_x += 1;
-    }
+    /*
+     * The "canvas coordinates" we're given look flat, but they're really
+     * isometric coordinates.  So we next want to convert them back to
+     * "flat" coordinates.  We'll keep the top-left corner of the canvas
+     * as (0,0) so that we have an easy frame of reference.  The
+     * transformation, which we're applying to a square tile, becomes:
+     *
+     *   1      1 4
+     *  234  ->  3
+     *   5      2 5
+     *
+     * This is (ignoring scale) the inverse of the transformation being
+     * done in map_pos_to_canvas_pos.
+     */
+    flat_canvas_x = canvas_x + canvas_y;
+    flat_canvas_y = canvas_y - canvas_x;
+
+    /*
+     * Now the (x0, y0) tile originally had its top corner at (0,0) and
+     * its bottom corner at (0,NORMAL_MAP_HEIGHT).  These are positions 1
+     * and 5 on the diagram above.  We have transformed these two positions:
+     *   (0,0) -> (0,0) -> (0,0)
+     *   (0,H) -> (0,W*H) -> (W*H,W*H)
+     * The tile is now a flat rectangle with these as its opposite corners.
+     * This makes it easy to tell what tile our coordinates belong to!
+     *
+     * Unfortunately, integer division doesn't work so well for negative
+     * numbers; -5/2==-2 while we're looking for -3.  So this is a bit
+     * uglier than it would otherwise be.
+     */
+    offset = NORMAL_TILE_WIDTH * NORMAL_TILE_HEIGHT / 2; /* FIXME - explain */
+    *map_x = DIVIDE(flat_canvas_x + offset,
+                   NORMAL_TILE_WIDTH * NORMAL_TILE_HEIGHT);
+    *map_y = DIVIDE(flat_canvas_y + offset,
+                   NORMAL_TILE_WIDTH * NORMAL_TILE_HEIGHT);
   } else {                     /* is_isometric */
-    *map_x = map_view_topleft_map_x + canvas_x / NORMAL_TILE_WIDTH;
-    *map_y = map_view_topleft_map_y + canvas_y / NORMAL_TILE_HEIGHT;
+    *map_x = canvas_x / NORMAL_TILE_WIDTH;
+    *map_y = canvas_y / NORMAL_TILE_HEIGHT;
   }
 
+  *map_x += map_view_topleft_map_x;
+  *map_y += map_view_topleft_map_y;
+
   /*
    * If we are outside the map find the nearest tile, with distance as
    * seen on the map.
@@ -247,9 +349,13 @@
        EXTRA_BOTTOM_ROW - map_view_map_height : *map_view_topleft_map_y;
   } else {
     int new_map_view_x0, new_map_view_y0;
+    int dx = map_view_map_width, dy = map_view_map_height;
+
+    if (mapview_orientation == 1 || mapview_orientation == 3)
+      SWAP(dx, dy);
 
-    new_map_view_x0 = map_adjust_x(map_x - map_view_map_width / 2);
-    new_map_view_y0 = map_adjust_y(map_y - map_view_map_height / 2);
+    new_map_view_x0 = map_adjust_x(map_x - dx / 2);
+    new_map_view_y0 = map_adjust_y(map_y - dy / 2);
     if (new_map_view_y0 >
        map.ysize + EXTRA_BOTTOM_ROW - map_view_map_height) {
       new_map_view_y0 =
Index: client/mapview_common.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.h,v
retrieving revision 1.3
diff -u -r1.3 mapview_common.h
--- client/mapview_common.h     2001/12/21 16:53:10     1.3
+++ client/mapview_common.h     2002/01/14 11:48:25
@@ -43,6 +43,9 @@
 void refresh_tile_mapcanvas(int x, int y, int write_to_screen);
 enum color_std get_grid_color(int x1, int y1, int x2, int y2);
 
+extern int mapview_orientation;
+void set_mapview_orientation(int new_orientation);
+
 int map_pos_to_canvas_pos(int map_x, int map_y,
                          int *canvas_x, int *canvas_y,
                          int map_view_topleft_map_x,
@@ -52,7 +55,9 @@
 void canvas_pos_to_map_pos(int canvas_x, int canvas_y,
                           int *map_x, int *map_y,
                           int map_view_topleft_map_x,
-                          int map_view_topleft_map_y);
+                          int map_view_topleft_map_y,
+                          int map_view_pixel_width,
+                          int map_view_pixel_height);
 
 void base_center_tile_mapcanvas(int map_x, int map_y,
                                int *map_view_topleft_map_x,
Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.59
diff -u -r1.59 tilespec.c
--- client/tilespec.c   2002/01/06 16:43:25     1.59
+++ client/tilespec.c   2002/01/14 11:48:26
@@ -49,6 +49,7 @@
 #include "options.h" /* for fill_xxx */
 
 #include "tilespec.h"
+#include "mapview_common.h"
 
 char *main_intro_filename;
 char *minimap_intro_filename;
@@ -1320,6 +1321,22 @@
   return sprs - save_sprs;
 }
 
+enum direction8 rotate_dir_cw(int steps, enum direction8 dir8)
+{
+  for ( ; steps > 0; steps--) {
+    dir8 = DIR_CW(dir8);
+  }
+  return dir8;
+}
+
+enum direction8 rotate_dir_ccw(int steps, enum direction8 dir8)
+{
+  for ( ; steps > 0; steps--) {
+    dir8 = DIR_CCW(dir8);
+  }
+  return dir8;
+}
+
 /**********************************************************************
   Fill in the sprite array for the tile at position (abs_x0,abs_y0)
 
@@ -1392,7 +1409,8 @@
     tspecial_near[dir] = S_NO_SPECIAL;
   }
   adjc_dir_iterate(abs_x0, abs_y0, x, y, dir) {
-    tspecial_near[dir] = map_get_special(x, y);
+    enum direction8 rot_dir = rotate_dir_cw(mapview_orientation * 2, dir);
+    tspecial_near[rot_dir] = map_get_special(x, y);
   } adjc_dir_iterate_end;
 
   /*
@@ -1402,9 +1420,10 @@
   ttype = map_get_terrain(abs_x0, abs_y0);
   for (dir = 0; dir < 8; dir++) {
     int x, y;
+    enum direction8 rot_dir = rotate_dir_cw(mapview_orientation * 2, dir);
 
     SAFE_MAPSTEP(x, y, abs_x0, abs_y0, dir);
-    ttype_near[dir] = map_get_terrain(x, y);
+    ttype_near[rot_dir] = map_get_terrain(x, y);
   }
 
   if(map.is_earth &&
@@ -1450,7 +1469,8 @@
        continue;
       }
       if (tspecial_near[dir8] & S_RIVER || ttype_near[dir8] == T_RIVER) {
-       *sprs++ = sprites.tx.river_outlet[dir8_to_dir4(dir8)];
+    enum direction8 rot_dir = rotate_dir_cw(mapview_orientation * 2, dir8);
+       *sprs++ = sprites.tx.river_outlet[dir8_to_dir4(rot_dir)];
       }
     }
   }
@@ -1596,13 +1616,16 @@
     memset(known, 0, sizeof(known));
     for (dir = 0; dir < 8; dir++) {
       int x1, y1;
+      enum direction8 rot_dir;
 
       if (!DIR_IS_CARDINAL(dir)) {
         continue;
       }
 
+      rot_dir = rotate_dir_cw(mapview_orientation * 2,dir);
+
       SAFE_MAPSTEP(x1, y1, abs_x0, abs_y0, dir);
-      known[dir8_to_dir4(dir)] = (tile_get_known(x1, y1) != TILE_UNKNOWN);
+      known[dir8_to_dir4(rot_dir)] = (tile_get_known(x1, y1) != TILE_UNKNOWN);
     }
 
     tileno =
Index: client/gui-gtk/mapview.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/mapview.c,v
retrieving revision 1.111
diff -u -r1.111 mapview.c
--- client/gui-gtk/mapview.c    2001/12/21 19:14:18     1.111
+++ client/gui-gtk/mapview.c    2002/01/14 11:48:28
@@ -254,8 +254,11 @@
 **************************************************************************/
 void get_map_xy(int canvas_x, int canvas_y, int *map_x, int *map_y)
 {
+  int width, height;
+
+  gdk_window_get_size(map_canvas->window, &width, &height);
   canvas_pos_to_map_pos(canvas_x, canvas_y, map_x, map_y, map_view_x0,
-                       map_view_y0);
+                       map_view_y0, width, height);
 }
 
 /**************************************************************************
@@ -572,6 +575,28 @@
 }
 
 
+enum direction8 diff_to_dir(int dx, int dy)
+{
+  if (dx == -1 && dy == -1)
+    return DIR8_NORTHWEST;
+  else if (dx == -1 && dy == 0)
+    return DIR8_WEST;
+  else if (dx == -1 && dy == 1)
+    return DIR8_SOUTHWEST;
+  else if (dx == 0 && dy == -1)
+    return DIR8_NORTH;
+  else if (dx == 0 && dy == 1)
+    return DIR8_SOUTH;
+  else if (dx == 1 && dy == -1)
+    return DIR8_NORTHEAST;
+  else if (dx == 1 && dy == 0)
+    return DIR8_EAST;
+  else if (dx == 1 && dy == 1)
+    return DIR8_SOUTHEAST;
+  assert(0);
+  return DIR8_NORTH;
+}
+
 /**************************************************************************
 Animates punit's "smooth" move from (x0,y0) to (x0+dx,y0+dy).
 Note: Works only for adjacent-square moves.
@@ -585,6 +610,7 @@
 {
   static struct timer *anim_timer = NULL; 
   int dest_x, dest_y, is_real;
+  enum direction8 dir;
 
   /* only works for adjacent-square moves */
   if ((dx < -1) || (dx > 1) || (dy < -1) || (dy > 1) ||
@@ -592,6 +618,11 @@
     return;
   }
 
+  /* Rotate.  Ugly. */
+  dir = diff_to_dir(dx, dy);
+  dir = rotate_dir_cw(mapview_orientation * 2, dir);
+  DIRSTEP(dx, dy, dir);
+
   if (punit == get_unit_in_focus() && hover_state != HOVER_NONE) {
     set_hover_state(NULL, HOVER_NONE);
     update_unit_info_label(punit);
@@ -893,10 +924,17 @@
     gdk_draw_line(overview_canvas->window, civ_gc,
                  Sx, Sy, Wx, Wy);
   } else {
+    int w = map_canvas_store_twidth;
+    int h = map_canvas_store_theight;
+    if (mapview_orientation == 1 || mapview_orientation == 3) {
+      int tmp = w;
+      w = h;
+      h = tmp;
+    }
     gdk_draw_rectangle(overview_canvas->window, civ_gc, FALSE,
                       
(overview_canvas_store_width-2*map_canvas_store_twidth)/2,
                       2*map_view_y0,
-                      2*map_canvas_store_twidth, 2*map_canvas_store_theight-1);
+                      2*w, 2*h-1);
   }
 }
 
@@ -1274,7 +1312,20 @@
     if (write_to_screen) {
       int canvas_x, canvas_y;
 
-      get_canvas_xy(x, y, &canvas_x, &canvas_y);
+      switch (mapview_orientation) {
+        case 0:
+          get_canvas_xy(x, y, &canvas_x, &canvas_y);
+          break;
+        case 1:
+          get_canvas_xy(x, y+height, &canvas_x, &canvas_y);
+          break;
+        case 2:
+          get_canvas_xy(x+width, y+height, &canvas_x, &canvas_y);
+          break;
+        case 3:
+          get_canvas_xy(x+width, y, &canvas_x, &canvas_y);
+          break;
+      }
       gdk_draw_pixmap(map_canvas->window, civ_gc, map_canvas_store,
                      canvas_x, canvas_y,
                      canvas_x, canvas_y,
@@ -1299,8 +1350,9 @@
                      map_view_y0 - map_canvas_store_twidth,
                      width, height, 1);
   } else {
+    int w = MAX(map_canvas_store_twidth, map_canvas_store_theight);
     update_map_canvas(map_view_x0, map_view_y0,
-                     map_canvas_store_twidth,map_canvas_store_theight, 1);
+                     w, w, 1);
   }
 
   show_city_descriptions();
Index: client/gui-gtk/menu.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/menu.c,v
retrieving revision 1.59
diff -u -r1.59 menu.c
--- client/gui-gtk/menu.c       2001/10/26 08:07:13     1.59
+++ client/gui-gtk/menu.c       2002/01/14 11:48:29
@@ -51,6 +51,8 @@
 
 #include "menu.h"
 
+#include "mapview_common.h"
+
 GtkItemFactory *item_factory=NULL;
 
 static void menus_rename(const char *path, char *s);
@@ -94,6 +96,7 @@
   MENU_VIEW_SHOW_FOCUS_UNIT,
   MENU_VIEW_SHOW_FOG_OF_WAR,
   MENU_VIEW_CENTER_VIEW,
+  MENU_VIEW_ROTATE_VIEW,
 
   MENU_ORDER_BUILD_CITY,     /* shared with BUILD_WONDER */
   MENU_ORDER_ROAD,           /* shared with TRADEROUTE */
@@ -289,6 +292,9 @@
   case MENU_VIEW_CENTER_VIEW:
     center_on_unit();
     break;
+  case MENU_VIEW_ROTATE_VIEW:
+    set_mapview_orientation( (mapview_orientation + 1) % 4 );
+    break;
   }
 }
 
@@ -632,6 +638,8 @@
        NULL,                   0,                                      
"<Separator>"   },
   { "/" N_("View") "/" N_("_Center View"),             "c",
        view_menu_callback,     MENU_VIEW_CENTER_VIEW                           
        },
+  { "/" N_("View") "/" N_("Rotate View"),              NULL,
+       view_menu_callback,     MENU_VIEW_ROTATE_VIEW                           
        },
   /* Orders menu ... */
   { "/" N_("_Orders"),                                 NULL,
        NULL,                   0,                                      
"<Branch>"      },
Index: common/map.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.c,v
retrieving revision 1.103
diff -u -r1.103 map.c
--- common/map.c        2001/12/13 19:13:16     1.103
+++ common/map.c        2002/01/14 11:48:30
@@ -1272,6 +1272,48 @@
   return (0 <= *y && *y < map.ysize);
 }
 
+
+/**************************************************************************
+Wraps (map_x, map_y) to be within the specified "window" if possible.  Note
+this "window" may or may not have anything to do with a GUI window; it's
+just like a fixed area floating over the underlying map.
+
+Note, (map_x, map_y) need not be real to fit under the "window": the
+window may float over unreal positions as well.  However, we will never
+wrap unreal coordinates to try to fit them within the window (any code
+which requires this functionality would most likely be flawed anyway).
+**************************************************************************/
+void find_representative_map_pos(int *map_x, int *map_y,
+                               struct win_map_context *win_mc)
+{
+  /* We absolutely don't wrap unreal coordinates. */
+  if (*map_y < 0 || *map_y >= map.ysize)
+    return;
+               
+  /* The implementation if this function is incredibly tricky,
+     although it doesn't seem like it would be.  The problem is
+     that not only is (*map_x,*map_y) not necessarily normal,
+     but neither is (win_mc->x0,win_mc->y0).  In this
+     implementation I assume that (win_mc->x0,win_mc->y0) is at
+     least _close_ to normal...which is the case right now.  Once
+     we allow wrapping in multiple directions, nothing outside
+     of linear algebra methods will be reasonable. */
+#define WRAP(x, size) ((x) % (size) >= 0 ?   \
+                      (x) % (size) :        \
+                      (x) % (size) + (size))
+       
+  if (win_mc->win_is_iso) {
+    /* This wrapping isn't complete, but it'll do for now. */
+    *map_x = WRAP(*map_x, map.xsize);
+    if (*map_x < win_mc->x0)
+      (*map_x) += map.xsize;
+  } else {
+    (*map_x) -= win_mc->x0;
+    *map_x = WRAP(*map_x, map.xsize);
+    (*map_x) += win_mc->x0;
+  }
+}
+
 /**************************************************************************
 Twiddle *x and *y to point the the nearest real tile, and ensure that the
 position is normalized.
Index: common/map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.h,v
retrieving revision 1.109
diff -u -r1.109 map.h
--- common/map.h        2002/01/06 16:43:26     1.109
+++ common/map.h        2002/01/14 11:48:31
@@ -301,6 +301,13 @@
    (y) == map.ysize-1 || (x) == map.xsize-1)
 
 int normalize_map_pos(int *x, int *y);
+struct win_map_context {
+       int x0, y0;
+       int width, height;
+       int win_is_iso;
+};
+void find_representative_map_pos(int *map_x, int *map_y,
+                               struct win_map_context *win_mc);
 void nearest_real_pos(int *x, int *y);
 void map_distance_vector(int *dx, int *dy, int x0, int y0, int x1, int y1);
 int map_num_tiles(void);
Index: common/shared.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/shared.h,v
retrieving revision 1.88
diff -u -r1.88 shared.h
--- common/shared.h     2001/12/21 11:17:43     1.88
+++ common/shared.h     2002/01/14 11:48:31
@@ -52,6 +52,13 @@
 
 #define BOOL_VAL(x) ((x)!=0)
 
+/*
+ * DIVIDE() divides and rounds down, rather than just divides and
+ * rounds toward 0.
+ */
+#define DIVIDE(n, d) \
+    ((n) % (d) < 0 ? (n) / (d) - 1 : (n) / (d))
+
 /* Deletes bit no in val,
    moves all bits larger than no one down,
    and inserts a zero at the top. */

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] demo: rotating the map view, Jason Short <=