Complete.Org: Mailing Lists: Archives: freeciv-dev: December 2002:
[Freeciv-Dev] Re: (PR#2439) city iterator cleanup
Home

[Freeciv-Dev] Re: (PR#2439) city iterator cleanup

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: jdorje@xxxxxxxxxxxxxxxxxxxxx
Cc: freeciv-dev@xxxxxxxxxxx
Subject: [Freeciv-Dev] Re: (PR#2439) city iterator cleanup
From: "rwetmore@xxxxxxxxxxxx via RT" <rt@xxxxxxxxxxxxxx>
Date: Sun, 1 Dec 2002 00:55:39 -0800
Reply-to: rt@xxxxxxxxxxxxxx

If you are going to touch the iterators, then it should probably be 
done right.

The attached header snippet can replace all current Freeciv adjacent
iterators in map.h and city.h. There is one core_iterate loop that 
handles all cases.

There is a single topology aware line in the _is_border_map_pos macro
(get_maptype() that can be deleted, and the following line that does
a native coordinate translation that doesn't exist yet in CVS. The
index macros in the whole_map_iterate may need to be added to make 
whole map work.

But apart from that and some changes to make GUI adjacent_iterate 
into adjacent_array_iterate, it should be pretty much plugin compatible
with some minor name consolidation.

People should look at the complete set of iterators to see which ones
are most useful, or conversely should go missing :-). It is fairly
straightforward to do a lot of topologically sophisticated adjacency
operations with this system, so it should also be considered as a step
towards gen_topologies and a cleanup of the diverse hard coded functional 
systems that are scattered throughout the code that will otherwise impede
future development.

If this survives the discussion/RFC stage, I would be happy to provide
a tested CVS patch that touches all the code where needed.

Cheers,
RossW
=====

At 10:28 AM 02/11/28 -0800, Jason Short via RT wrote:
>
>
>This patch was suggested by Per:
>
>It removes city_radius_iterate.  This macro is not used, probably
>shouldn't ever be used, and cannot be used because there is no
>city_radius_iterate_end.
>
>It redefines map_city_radius_iterate to use city_map_checked_iterate.  I
>also change the parameter names, and add a comment on this iterator.
>
>jason
>
>
>Index: common/city.h
>===================================================================
>RCS file: /home/freeciv/CVS/freeciv/common/city.h,v
>retrieving revision 1.118
>diff -u -r1.118 city.h
>--- common/city.h      2002/11/25 19:18:09     1.118
>+++ common/city.h      2002/11/28 18:25:52
>@@ -128,30 +128,14 @@
>   } city_map_iterate_outwards_end    \
> }
>
>-/* Iterate a city radius: (dx,dy) centered on (0,0) */
>-#define city_radius_iterate(dx, dy) \
>-  for (dy = -(int)(CITY_MAP_SIZE/2); dy<(int)(CITY_MAP_SIZE/2); dy++) \
>-    for (dx = -(int)(CITY_MAP_SIZE/2); dx<(int)(CITY_MAP_SIZE/2); dx++) \
>-      if (! ((dx == -(int)(CITY_MAP_SIZE/2) || dx ==
(int)(CITY_MAP_SIZE/2)) && \
>-           (dy == -(int)(CITY_MAP_SIZE/2) || dy == (int)(CITY_MAP_SIZE/2))) )
>
>
>-#define map_city_radius_iterate(city_x, city_y, x_itr, y_itr)     \
>+/* Does the same thing as city_map_checked_iterate, but keeps the city
>+ * coordinates hidden. */
>+#define map_city_radius_iterate(city_x, city_y, map_x, map_y)     \
> {                                                                 \
>-  int x_itr, y_itr;                                               \
>-  int MCMI_x, MCMI_y;                                             \
>-  for (MCMI_x = 0; MCMI_x < CITY_MAP_SIZE; MCMI_x++) {            \
>-    for (MCMI_y = 0; MCMI_y < CITY_MAP_SIZE; MCMI_y++) {          \
>-      if (! ((MCMI_x == 0 || MCMI_x == (CITY_MAP_SIZE-1))         \
>-           && (MCMI_y == 0 || MCMI_y == (CITY_MAP_SIZE-1))) ) { \
>-      if(!base_city_map_to_map(&x_itr, &y_itr, city_x,    \
>-                                     city_y, MCMI_x, MCMI_y))   \
>-        continue;
>+  city_map_checked_iterate(city_x, city_y, _cx, _cy, map_x, map_y) { 
>
> #define map_city_radius_iterate_end                               \
>-      }                                                           \
>-    }                                                             \
>-  }                                                               \
>+  } city_map_checked_iterate_end;                                 \
> }
>
>
/*
 * A "border position" is any one that has adjacent positions that are
 * not normal/proper.
 *
 * is_border_map_pos(x,y) and
 * is_border2_map_pos(x,y) are TRUE if position(x,y) is on the outer border
 *   of the native rectangular map or is outside the map. They are useful as
 *   a fast check for tile validity inside adjacent loops. In the second
 *   version the border is extended to a depth of two tiles.
 *
 * _is_border_map_pos(N,X,Y) is a utility macro that treats border depth
 *   as a parameter, i.e. within "N" tiles of the real border. N can be
 *   considered a standard map distance, thus may be more than N tiles in
 *   some topologies. Negative distances are allowed. If the distance is
 *   zero, no check is made and the result is FALSE.
 */
#define _is_border_map_pos(N,MX,MY)                                           \
( ((_FC_MAP_N = (N)) != 0) &&                                                 \
  ( _FC_MAP_N = (_FC_MAP_N < 0 ? -_FC_MAP_N : _FC_MAP_N),                     \
    _FC_MAP_N+= (get_maptype(MAP_TYPE_ISO) ? _FC_MAP_N+1 : 0),                \
    map_to_native_pos(&_FC_MAP_X, &_FC_MAP_Y, (MX), (MY)),                    \
    (  !(_FC_MAP_N <= _FC_MAP_Y && _FC_MAP_Y < map.ysize-_FC_MAP_N)           \
    || !(_FC_MAP_N <= _FC_MAP_X && _FC_MAP_X < map.xsize-_FC_MAP_N) ) ) )

#define is_border_map_pos(MX, MY)       _is_border_map_pos(1, (MX), (MY))
#define is_border2_map_pos(MX, MY)      _is_border_map_pos(2, (MX), (MY))

#define IS_BORDER_MAP_POS(MX, MY, N)    _is_border_map_pos((N), (MX), (MY))

/**********************************************************************/

/*
 * Data driven offset arrays when used to compute neighboring tiles, can
 * greatly simplify internal direction handling and adjacent loops.
 *
 * Using rDir = 0, 1, ... 7 and
 *   x1 = x + DIR_DX2[rDir];
 *   y1 = y + DIR_DY2[rDir];
 * will give you the tiles in clockwise rotation order as shown below.
 * 
 * -------
 * |0|1|2|  NW (== 0) is the origin
 * |-+-+-|
 * |7| |3|
 * |-+-+-|
 * |6|5|4|
 * ------- 
 * 
 * -------  
 * |0|1|2|  This is the array order used for example, by GUI tiling,
 * |-+-+-|  If the direction enums are given by the previous numbers,
 * |3| |4|  then a simple translation can be done to iterate in any
 * |-+-+-|  order such as aDir = 0, 1, ... 7 order at right.
 * |5|6|7|    DIR_ator[] = { 0, 1, 2, 7, 3, 6, 5, 4 }
 * -------    rDir = DIR_ator[aDir];
 * 
 * Array order is primarily used to store graphics in a continuous NW-SE
 * direction where overlaps are updated only on the leading edge.
 * 
 * Data driven is both efficient in the inner loop as data lookup rather
 * than code is used to control the flow. It is also highly extensible to
 * new cases with no code changes other than substituion of the data
 * controls. For example, different topologies may have different
 * adjacent ordering or offsets. The same loop code with a topology
 * aware lookup to set its data controls will run any existing (and
 * as yet unimplemented) topologies without code change.
 *
 * The single core_iterate loop below, manages all standard map, iso
 * map and city map adjacent iterations currently used by freeciv.
*/

/**
 * Macros for moving around the 1st and 2nd square in NWSE directions
 * There are 8n directions or edge tiles in the n'th square outwards,
 *   (2n+1)^2 => 1, 9, 25 sized squares, (2n+1)^2 - (2n-1)^2= 8n.
 *
 * The Delta macros step to direction "n", wrapping at high/low ends.
 *
 * Iterate macros below use a single indexed while loop based on the
 *   following known ordering, with access to the index (i.e. _nn),
 *   or using the "_dir_iterate" form to provide a custom index.
 *   Example: if _nn= 0, you are NW of your start.
 *
 * All allocate the user's chosen x,y,dir variables within the (core) loop.
 * All treat arguments other than x,y,dir as single use expressions.
 * All use a single loop on the index value _nn or supplied dir.
 * All blocks will be correctly wrapped in the x dimension at the
 *  map.xsize boundary, and off map y values will be skipped. With
 *  properly set normalize_map_pos, this will be true for a select
 *  set of other map topologies (see above). This is implemented by
 *  running within an inner if clause, so "} else {" can be used to
 *  process invalid map positions.
 *
 * Depends on map.xsize, map.ysize and normalize_map_pos
 *
 * TODO: extend this to the third square or 7x7 to handle most local ops
*/

/* Extern definitions should visible/valid in all locations           */
extern const int DIR_ctor[], DIR_cator[], DIR_ator[], DIR_rtoa[];
extern const int DIR_dx[], DIR_dy[], DIR_off[];
extern const char *DIR_dn[];
#define DIR_DN2 (DIR_dn+1)

extern const char* dir_get_name(int dir);

/* Actual variable allocations happen once in map.c                   */
#ifdef FC__MAP_C

/* Array of offsets starting at the NW corner and moving clockwise,   */
/* including the origin. Note, first 1, 9, and 25 == square iteration */
/* WARNING: Code below depends on the clockwise order starting at NW. */
const int DIR_off[] = {
    0,  1,  9, 25, 49,
};
const int DIR_dx[] = {
    0,
   -1,  0,  1,  1,  1,  0, -1, -1,
   -2, -1,  0,  1,  2,  2,  2,  2,  2,  1,  0, -1, -2, -2, -2, -2,
};
const int DIR_dy[] = {
    0,
   -1, -1, -1,  0,  1,  1,  1,  0,
   -2, -2, -2, -2, -2, -1,  0,  1,  2,  2,  2,  2,  2,  1,  0, -1,
};
const char *DIR_dn[] = {
   "X",
   "NW", "N", "NE", "E", "SE", "S", "SW", "W",
   "NW2", "NNW", "N2", "NNE", "NE2", "ENE", "E2", "ESE",
   "SE2", "SSE", "S2", "SSW", "SW2", "WSW", "W2", "WNW",
};


/* array to rotational order mapping, rDir = DIR_ator[aDir]           */
const int DIR_ator[] = {
    0,
    1,  2,  3,  8,  4,  7,  6,  5,
    9, 10, 11, 12, 13, 24, 14, 23, 15, 22, 16, 21, 20, 19, 18, 17,
};

/* rotational to array order mapping, aDir = DIR_rtoa[rDir]           */
const int DIR_rtoa[] = {
    0,
    1,  2,  3,  5,  8,  7,  6,  4,
    9, 10, 11, 12, 13, 15, 17, 19, 24, 23, 22, 21, 20, 18, 16, 14,
};


/* city order to rotational mapping, rDir = DIR_ctor[cDir]            */
const int DIR_ctor[] = {
    0,                                                 /* centre      */
    2,  4,  6,  8,                                     /* cartesian 1 */
    1,  3,  5,  7,                                     /* diagonal  1 */
   11, 15, 19, 23,                                     /* cartesian 2 */
   10, 12, 14, 16, 18, 20, 22, 24,                     /* knights   2 */
    9, 13, 17, 21,                            /* diagonal  2 - unused */
};

/* city array order to rotational mapping, rDir = DIR_cator[caDir]    */
const int DIR_cator[] = {
 /* 9,*/ 10, 11, 12, /*13,*/
     24,  1,  2,  3, 14,
     23,  8,  0,  4, 15, 
     22,  7,  6,  5, 16, 
 /*21,*/ 20, 19, 18, /*17,*/
      9, 13, 17, 21,                          /* diagonal  2 - unused */
};
#endif /* FC__MAP_C */


/* Direction enum for rotation order adjacent rings */
enum DirectionR {
  DIR_rUNKNOWN        = -1, /* not-a-direction */

  DIR_rCENTRE         = 0,  /* centre tile */

  DIR_rNORTHWEST      = 0,  /* first adjacent ring at offset 1 */
  DIR_rNORTH          = 1,
  DIR_rNORTHEAST      = 2,
  DIR_rEAST           = 3,
  DIR_rSOUTHEAST      = 4,
  DIR_rSOUTH          = 5,
  DIR_rSOUTHWEST      = 6,
  DIR_rWEST           = 7,

  DIR_rNORTHWEST2     = 0,  /* second ring at offset 9 */
  DIR_rNORTHNORTHWEST = 1,
  DIR_rNORTH2         = 2,
  DIR_rNORTHNORTHEAST = 3,
  DIR_rNORTHEAST2     = 4,
  DIR_rEASTNORTHEAST  = 5,
  DIR_rEAST2          = 6,
  DIR_rEASTSOUTHEAST  = 7,
  DIR_rSOUTHEAST2     = 8,
  DIR_rSOUTHSOUTHEAST = 9,
  DIR_rSOUTH2         = 10,
  DIR_rSOUTHSOUTHWEST = 11,
  DIR_rSOUTHWEST2     = 12,
  DIR_rWESTSOUTHWEST  = 13,
  DIR_rWEST2          = 14,
  DIR_rWESTNORTHWEST  = 15,
};


/*
 * Adjacent (or ring 1) utility macros 
 * - Cartesian tests, checked direction, reverse and bit reps
*/
#define DIR_IS_CARDINAL(dir) ((dir)&1)
#define DIR_CHECKED(dir)     ((dir)&7)
#define DIR_REVERSE(dir)     ((dir)^4)
#define DIR_BIT(dir)         (1<<DIR_CHECKED(dir))

#define DIR_CCW(dir)         DIR_CHECKED(dir-1)
#define DIR_CW(dir)          DIR_CHECKED(dir+1)

#define DIR_CCW_N(dir, n)    DIR_CHECKED(dir-n)
#define DIR_CW_N(dir, n)     DIR_CHECKED(dir+n)


/* These macros are used to step to adjacent tiles and normalize */
#define _map_step(px, py, dir)                                 \
( *(px) = DIR_dx[DIR_off[1]+(dir)],                            \
  *(py) = DIR_dy[DIR_off[1]+(dir)] )

#define map_step(px, py, x0, y0, dir)                          \
( _map_step(&_FC_MAP_X, &_FC_MAP_Y, dir),                      \
  *(px) = (x0) + _FC_MAP_X,                                    \
  *(py) = (y0) + _FC_MAP_Y,                                    \
  normalize_map_pos((px),(py)) )

/* These macros are used to step to tiles in the n'th ring and normalize */
#define _map_stepN(px, py, dir, nr)                            \
( *(px) = DIR_dx[DIR_off[nr]+(dir)],                           \
  *(py) = DIR_dy[DIR_off[nr]+(dir)] )

#define map_stepN(px, py, x0, y0, dir, nr, is_border)          \
( _map_stepN(&_FC_MAP_X, &_FC_MAP_Y, (dir), (nr)),             \
  *(px) = (x0) + _FC_MAP_X,                                    \
  *(py) = (y0) + _FC_MAP_Y,                                    \
  (!(is_border) || normalize_map_pos((px),(py))) )


/**
 * Core adjacent iteration loop
 *
 * This returns a set of {x, y, dir} values based on the control settings,
 * (x, y) a normalized map position, dir as the ring offset for ring nr.
 * dir can be converted to/from a ring offset using DIR_off[ring].
 * Unreal positions can be retrieved from the "} else {" clause.
 *
 * Most users need only use the canned iterators below. Details here
 * are for those intrepid coders wanting to add new iterators.
 *
 * Controls:
 * x0,y0    - is the centre map position
 * dtor     - maps an arbitrary direction set to rotational directions
 * n0,n1,ni - are the start, end and increment for the dtor index loop
 * nr       - is the ring number for border checks as in ...
 *            if (x0,y0) is within nr of the border, normalization is on
 * Notes:
 * 0)  x, y, dir are allocated and accessible within the loop only.
 *     Users should *not* use any other internal locals in their code.
 * 1)  reference arguments accessed once by local assignment
 * 2)  border guard avoids full map checks except where an increment
 *     could force an out of bounds condition. A zero distance turns 
 *     off all border checks (see the code/macro definition).
 * 3)  if nr is negative, the border check is done and nr is set to 0
 *     i.e. directions are absolute and not ring offsets (see citymap).
 * 4)  combination of dtor and loop controls allows *any* iteration set
 *     or ordering with output {x,y,dir} still in standard map form.
 * 5)  When constant args are used, a good optimizing compiler will 
 *     completely eliminate significant chunks of macro codepaths.
 * 6)  this is a singly nested loop - "break" works!

 */
#define core_iterate(dtor, nr, n0, n1, ni, x0, y0, x, y, dir) {        \
  const int *_dtor = (dtor);                                           \
  int _dir = (nr);                                                     \
  const int  _n1= (n1), _ni= (ni), _x0= (x0), _y0= (y0);               \
  const bool _border = _is_border_map_pos(_dir, _x0, _y0);             \
  const int  _nr = (_dir < 0 ? 0 : _dir);                              \
                                                                       \
  for (_dir = (n0); _dir < _n1; _dir += _ni) {                         \
    int x, y, dir;                                                     \
    dir = (_dtor ? _dtor[DIR_off[_nr]+_dir]-DIR_off[nr] : _dir);       \
    if( map_stepN(&x, &y, _x0, _y0, dir, _nr, _border) ) {

#define core_iterate_end                                               \
    }                                                                  \
  }                                                                    \
}


/* Cartesian N, E, S, W iteration */
#define cartesian_adjacent_iterate(x0, y0, x, y)                       \
  core_iterate(NULL, 1, 1, 8, 2, x0, y0, x, y, _nn)

#define cartesian_adjacent_iterate_end                                 \
  core_iterate_end

/* Cartesian diagonal iteration NW, NE, SE, SW */
#define cartesian_diagonal_iterate(x0, y0, x, y)                       \
  core_iterate(NULL, 1, 0, 8, 2, x0, y0, x, y, _nn)

#define cartesian_diagonal_iterate_end                                 \
  core_iterate_end


/* full 1st square iteration NW, N, ... W */
#define adjc_iterate(x0, y0, x, y)                                     \
  core_iterate(NULL, 1, 0, 8, 1, x0, y0, x, y, _nn)

#define adjc_iterate_end                                               \
  core_iterate_end


/* Cartesian N, E, S, W iteration: 2nd square*/
#define cartesian_adjacent_iterate2(x0, y0, x, y)                      \
  core_iterate(NULL, 2, 2, 16, 4, x0, y0, x, y, _nn)

#define cartesian_adjacent_iterate2_end                                \
  core_iterate_end

/* Cartesian diagonal iteration: 2nd square */
#define cartesian_diagonal_iterate2(x0, y0, x, y)                      \
  core_iterate(NULL, 2, 0, 16, 4, x0, y0, x, y, _nn)

#define cartesian_diagonal_iterate2_end                                \
  core_iterate_end

/* Cartesian knights iteration: 2nd square */
#define cartesian_knights_iterate2(x0, y0, x, y)                       \
  core_iterate(NULL, 2, 1, 16, 2, x0, y0, x, y, _nn)

#define cartesian_knights_iterate2_end                                 \
  core_iterate_end


/* full iteration: 2nd square */
#define adjc_iterate2(x0, y0, x, y)                                    \
  core_iterate(NULL, 2, 0, 16, 1, x0, y0, x, y, _nn)

#define adjc_iterate2_end                                              \
  core_iterate_end


/* Cartesian N, E, S, W dir iteration */
#define cartesian_adjacent_dir_iterate(x0, y0, x, y, dir)              \
  core_iterate(NULL, 1, 1, 8, 2, x0, y0, x, y, dir)

#define cartesian_adjacent_dir_iterate_end                             \
  core_iterate_end

/* Cartesian diagonal dir iteration NW, NE, SE, SW */
#define cartesian_diagonal_dir_iterate(x0, y0, x, y, dir)              \
  core_iterate(NULL, 1, 0, 8, 2, x0, y0, x, y, dir)

#define cartesian_diagonal_dir_iterate_end                             \
  core_iterate_end


/* full 1st square dir iteration W, SW, ... NW */
#define adjc_dir_iterate(x0, y0, x, y, dir)                            \
  core_iterate(NULL, 1, 0, 8, 1, x0, y0, x, y, dir)

#define adjc_dir_iterate_end                                           \
  core_iterate_end


/* Cartesian N, E, S, W dir iteration: 2nd square*/
#define cartesian_adjacent_dir_iterate2(x0, y0, x, y, dir)             \
  core_iterate(NULL, 2, 2, 16, 4, x0, y0, x, y, dir)

#define cartesian_adjacent_dir_iterate2_end                            \
  core_iterate_end

/* Cartesian diagonal dir iteration: 2nd square */
#define cartesian_diagonal_dir_iterate2(x0, y0, x, y, dir)             \
  core_iterate(NULL, 2, 0, 16, 4, x0, y0, x, y, dir)

#define cartesian_diagonal_dir_iterate2_end                            \
  core_iterate_end

/* Cartesian knights dir iteration: 2nd square */
#define cartesian_knights_dir_iterate2(x0, y0, x, y, dir)              \
  core_iterate(NULL, 2, 1, 16, 2, x0, y0, x, y, dir)

#define cartesian_knights_dir_iterate2_end                             \
  core_iterate_end


/* full dir iteration: 2nd square */
#define adjc_dir_iterate2(x0, y0, x, y, dir)                           \
  core_iterate(NULL, 2, 0, 16, 1, x0, y0, x, y, dir)

#define adjc_dir_iterate2_end                                          \
  core_iterate_end


/* citymap checked iteration over rings 0, 1, 2 */
#define city_map_checked_iterate(x0, y0, cx, cy, mx, my) {             \
  int cx, cy, _xo= CITY_MAP_SIZE/2, _yo= CITY_MAP_SIZE/2;              \
  core_iterate(DIR_ctor, -2, 0, 21, 1, (x0), (y0), mx, my, _nn)        \
    map_stepN(&cx, &cy, _xo, _yo, _nn, 0, 0);

#define city_map_checked_iterate_end                                   \
  core_iterate_end                                                     \
}

/* citymap checked iteration over rings 1, 2 (i.e. omit city centre) */
#define city_map_checked_iterate_1(x0, y0, cx, cy, mx, my) {           \
  int cx, cy, _xo= CITY_MAP_SIZE/2, _yo= CITY_MAP_SIZE/2;              \
  core_iterate(DIR_ctor, -2, 1, 21, 1, (x0), (y0), mx, my, _nn)        \
    map_stepN(&cx, &cy, _xo, _yo, _nn, 0, 0);

#define city_map_checked_iterate_1_end                                 \
  core_iterate_end                                                     \
}


/* citymap iteration over rings 0, 1, 2 */
#define city_map_iterate(cx, cy) {                                     \
  const int _xo= CITY_MAP_SIZE/2, _yo= CITY_MAP_SIZE/2;                \
  core_iterate(DIR_ctor, 0, 0, 21, 1, _xo, _yo, cx, cy, _nn)

#define city_map_iterate_end                                           \
  core_iterate_end                                                     \
}

/* citymap iteration over rings 1, 2 (i.e. omit city centre) */
#define city_map_iterate_1(cx, cy) {                                   \
  const int _xo= CITY_MAP_SIZE/2, _yo= CITY_MAP_SIZE/2;                \
  core_iterate(DIR_ctor, 0, 1, 21, 1, _xo, _yo, cx, cy, _nn)

#define city_map_iterate_1_end                                         \
  core_iterate_end                                                     \
}


/* citymap radius iteration over rings 0, 1, 2 - centered on (0,0) */
#define city_radius_iterate(cx, cy)                                    \
  core_iterate(DIR_ctor, 0, 0, 21, 1, 0, 0, cx, cy, _nn)

#define city_radius_iterate_end                                        \
  core_iterate_end


/* citymap array checked iteration over rings 0, 1, 2 */
#define city_map_array_checked_iterate(x0, y0, cx, cy, mx, my) {       \
  int cx, cy, _xo= CITY_MAP_SIZE/2, _yo= CITY_MAP_SIZE/2;              \
  core_iterate(DIR_cator, -2, 0, 21, 1, (x0), (y0), mx, my, _nn)       \
    map_stepN(&cx, &cy, _xo, _yo, _nn, 0, 0);

#define city_map_array_checked_iterate_end                             \
  core_iterate_end                                                     \
}

/* citymap array iteration over rings 0, 1, 2 */
#define city_array_iterate(cx, cy)                                     \
  const int _xo= CITY_MAP_SIZE/2, _yo= CITY_MAP_SIZE/2;                \
  core_iterate(DIR_cator, 0, 0, 21, 1, _xo, _yo, cx, cy, _nn)

#define city_map_iterate_1_end                                         \
  core_iterate_end                                                     \
}


/* square iteration over rings 0, 1, 2 */
#define square_iterate2(x0, y0, x, y, dir)                             \
  core_iterate(NULL, -2, 0, 25, 1, (x0), (y0), x, y, dir)

#define square_iterate2_end                                            \
  core_iterate_end


/* 
 * Generic 2n+1 square array iteration (i.e. within map distance n)
 *
 * Square iteration is mostly done to map a fixed distance local area.
 * Since distance is calculated in standard map coordinates, even for
 * isometric maps, results must be checked for realness + normalized.
 *
 * A negative distance turns off all map normalization checks, as 
 * when iterating a 3x3 citymap display including corners.
 *   square_iterate(CITY_MAP_SIZE/2, CITY_MAP_SIZE/2, 2, cx, cy)
 *
 * It too uses a single loop.
 */
#define square_iterate(x0, y0, n, x1, y1) {                            \
  int x1, y1 = (n), _x0 = (x0), _y0 = (y0);                            \
  const bool _border = _is_border_map_pos(y1, _x0, _y0);               \
  const int  _nn = (y1 > 0 ? y1 : -y1);                                \
  const int  _yn = _y0 + _nn, _xn = _x0 + _nn;                         \
  _y0 = _y0 - _nn, _x0 = _x0 - _nn - 1;                                \
  while( 1 ) {                                                         \
    if( ++_x0 > _xn ) {                                                \
      if( ++_y0 > _yn )                                                \
        break;                                                         \
      _x0 = _xn - _nn - _nn;                                           \
    }                                                                  \
    x1 = _x0, y1 = _y0;                                                \
    if( !_border || normalize_map_pos(&x1, &y1) ) {

#define square_iterate_end                                             \
    }                                                                  \
  }                                                                    \
}


/* 
 * circle iteration is a mistake - constant map distance is a square.
 *
 * Note: this is NOT a single loop.
 */
#define circle_iterate(x0, y0, rsq, x1, y1) {                          \
  int x1, y1, _xn, _yn, _rsq = (rsq), _x0 = (x0), _y0 = (y0);          \
  int _nn = (int) sqrt(rsq);                                           \
  bool _border = _is_border_map_pos(_nn, _x0, _y0);                    \
  for( _yn = -_nn; _yn < _nn; _yn++ )                                  \
  for( _xn = -_nn; _xn < _nn; _xn++ ) {                                \
    x1 = _xn + _x0, y1 = _yn + _y0;                                    \
    if( (_yn*_yn + _xn*_xn <= _rsq)                                    \
     && (!_border || normalize_map_pos(&x1,&y1)) ) {

#define circle_iterate_end                                             \
    }                                                                  \
  }                                                                    \
}


/**
 * Optimized whole map iterate (counts down the linear index with
 * conversion to standard map coordinates).
 *
 * It uses a single loop (on the linear offset xy = map.xsize*y + x).
 */
#define whole_map_iterate(map_x, map_y)                                \
  whole_map_xy_iterate(_xy, _x, _y, map_x, map_y)

#define whole_map_iterate_end                                          \
  whole_map_xy_iterate_end


/* Expose native and index coordinates as well as standard map (x,y). */
#define whole_map_xy_iterate(xy, xn, yn, mx, my) {                     \
  int mx, my, xn, yn, xy= map.ysize*map.xsize;                         \
  while( xy-- > 0 ) {                                                  \
    if( index_to_map_pos(&mx, &my, &xn, &yn, xy) ) {

#define whole_map_xy_iterate_end                                       \
    }                                                                  \
  }                                                                    \
}






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