[Freeciv-Dev] Re: (PR#4004) A canvas_iterate macro for mapview
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: |
undisclosed-recipients: ; |
Subject: |
[Freeciv-Dev] Re: (PR#4004) A canvas_iterate macro for mapview |
From: |
"Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx> |
Date: |
Sat, 24 Apr 2004 14:25:09 -0700 |
Reply-to: |
rt@xxxxxxxxxxx |
<URL: http://rt.freeciv.org/Ticket/Display.html?id=4004 >
Jason Short wrote:
> And here's a bigger better patch.
Ok, this may be the final patch.
The first patch includes the iterator only. This is intended for inclusion.
The second patch includes some users. This shows what I'd like to do
with this macro (good things), but it's not really final yet.
I wrote some nice documentation of the iso-view iteration. Take a look
at it at http://rt.freeciv.org/Ticket/Attachment/51374/37363/isogrid.png.
jason
Index: client/mapview_common.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.h,v
retrieving revision 1.58
diff -u -r1.58 mapview_common.h
--- client/mapview_common.h 23 Apr 2004 16:43:13 -0000 1.58
+++ client/mapview_common.h 24 Apr 2004 21:03:31 -0000
@@ -136,6 +136,89 @@
UPDATE_MAP_CANVAS_VISIBLE = 2
};
+/* Iterate over all map tiles that intersect with the given GUI rectangle.
+ *
+ * gui_x0, gui_y0: gives the GUI origin of the rectangle.
+ * width, height: gives the GUI width and height of the rectangle. These
+ * values may be negative.
+ *
+ * map_x, map_y: variables that will give the current tile of iteration.
+ * These coordinates are unnormalized.
+ *
+ * draw: A variable that tells which parts of the tiles overlap with the
+ * GUI rectangle. Only applies in iso-view.
+ *
+ * The classic-view iteration is pretty simple. Documentation of the
+ * iso-view iteration is at
+ * http://rt.freeciv.org/Ticket/Attachment/51374/37363/isogrid.png.
+ */
+#define gui_rect_iterate(gui_x0, gui_y0, width, height, map_x, map_y, draw) \
+{ \
+ int _gui_x0 = (gui_x0), _gui_y0 = (gui_y0); \
+ int _width = (width), _height = (height); \
+ if (_width < 0) { \
+ _gui_x0 += _width; \
+ _width = -_width; \
+ } \
+ if (_height < 0) { \
+ _gui_y0 += _height;
\
+ _height = -_height;
\
+ } \
+ if (_width > 0 && _height > 0) { \
+ int W = (is_isometric ? (NORMAL_TILE_WIDTH / 2) : NORMAL_TILE_WIDTH); \
+ int H = (is_isometric ? (NORMAL_TILE_HEIGHT / 2) : NORMAL_TILE_HEIGHT); \
+ int GRI_x0 = DIVIDE(_gui_x0, W), GRI_y0 = DIVIDE(_gui_y0, H); \
+ int GRI_x1 = DIVIDE(_gui_x0 + _width + W - 1, W); \
+ int GRI_y1 = DIVIDE(_gui_y0 + _height + H - 1, H); \
+ int GRI_itr, GRI_x_itr, GRI_y_itr, map_x, map_y; \
+ enum draw_type draw; \
+ int count; \
+ if (is_isometric) {
\
+ /* Extra half-tile of UNIT_TILE_HEIGHT. */ \
+ GRI_y1++;
\
+ /* Tiles to the left/above overlap with us. */ \
+ GRI_x0--;
\
+ GRI_y0--;
\
+ } \
+ count = (GRI_x1 - GRI_x0) * (GRI_y1 - GRI_y0); \
+ for (GRI_itr = 0; GRI_itr < count; GRI_itr++) { \
+ GRI_x_itr = GRI_x0 + (GRI_itr % (GRI_x1 - GRI_x0)); \
+ GRI_y_itr = GRI_y0 + (GRI_itr / (GRI_x1 - GRI_x0)); \
+ if (is_isometric) { \
+ if ((GRI_x_itr + GRI_y_itr) % 2 != 0) { \
+ continue; \
+ } \
+ draw = 0; \
+ if (GRI_x_itr > GRI_x0) { \
+ draw |= D_L; \
+ } \
+ if (GRI_x_itr < GRI_x1 - 1) { \
+ draw |= D_R; \
+ } \
+ if (GRI_y_itr > GRI_y0) { \
+ draw |= D_M; \
+ } \
+ if (GRI_y_itr > GRI_y0 + 1) { \
+ draw |= D_T; \
+ } \
+ if (GRI_y_itr < GRI_y1 - 2) { \
+ draw |= D_B; \
+ } \
+ assert((draw & (D_L | D_R)) && (draw & (D_T | D_M | D_B))); \
+ assert((draw & D_M) || !((draw & D_T) && (draw & D_B))); \
+ map_x = (GRI_x_itr + GRI_y_itr) / 2; \
+ map_y = (GRI_y_itr - GRI_x_itr) / 2; \
+ } else { \
+ draw = D_FULL; \
+ map_x = GRI_x_itr; \
+ map_y = GRI_y_itr; \
+ }
+
+#define gui_rect_iterate_end \
+ } \
+ } \
+}
+
void refresh_tile_mapcanvas(int x, int y, bool write_to_screen);
enum color_std get_grid_color(int x1, int y1, int x2, int y2);
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.106
diff -u -r1.106 mapview_common.c
--- client/mapview_common.c 24 Apr 2004 17:20:17 -0000 1.106
+++ client/mapview_common.c 24 Apr 2004 21:04:16 -0000
@@ -56,13 +56,16 @@
**************************************************************************/
void refresh_tile_mapcanvas(int x, int y, bool write_to_screen)
{
+ int canvas_x, canvas_y;
+
assert(is_real_map_pos(x, y));
if (!normalize_map_pos(&x, &y)) {
return;
}
- if (tile_visible_mapcanvas(x, y)) {
- update_map_canvas(x, y, 1, 1, FALSE);
+ if (map_to_canvas_pos(&canvas_x, &canvas_y, x, y)) {
+ canvas_y += NORMAL_TILE_HEIGHT - UNIT_TILE_HEIGHT;
+ update_map_canvas(canvas_x, canvas_y, UNIT_TILE_WIDTH, UNIT_TILE_HEIGHT);
if (update_city_text_in_refresh_tile
&& (draw_city_names || draw_city_productions)) {
@@ -1268,132 +1271,43 @@
x, y, width, and height are in map coordinates; they need not be
normalized or even real.
**************************************************************************/
-void update_map_canvas(int x, int y, int width, int height,
- bool write_to_screen)
+void update_map_canvas(int canvas_x, int canvas_y, int width, int height)
{
- int canvas_start_x, canvas_start_y;
+ const int gui_x = mapview_canvas.gui_x0 + canvas_x;
+ const int gui_y = mapview_canvas.gui_y0 + canvas_y;
freelog(LOG_DEBUG,
- "update_map_canvas(pos=(%d,%d), size=(%d,%d), write_to_screen=%d)",
- x, y, width, height, write_to_screen);
+ "update_map_canvas(pos=(%d,%d), size=(%d,%d))",
+ canvas_x, canvas_y, width, height);
if (is_isometric) {
- int x_itr, y_itr, i;
-
- /* First refresh the tiles above the area to remove the old tiles'
- * overlapping graphics. */
- put_tile_iso(x - 1, y - 1, D_B_LR); /* top_left corner */
-
- for (i = 0; i < height - 1; i++) { /* left side - last tile. */
- put_tile_iso(x - 1, y + i, D_MB_LR);
- }
- put_tile_iso(x - 1, y + height - 1, D_TMB_R); /* last tile left side. */
-
- for (i = 0; i < width - 1; i++) {
- /* top side */
- put_tile_iso(x + i, y - 1, D_MB_LR);
- }
- if (width > 1) {
- /* last tile top side. */
- put_tile_iso(x + width - 1, y - 1, D_TMB_L);
- } else {
- put_tile_iso(x + width - 1, y - 1, D_MB_L);
- }
-
- /* Now draw the tiles to be refreshed, from the top down to get the
- * overlapping areas correct. */
- for (x_itr = x; x_itr < x + width; x_itr++) {
- for (y_itr = y; y_itr < y + height; y_itr++) {
- put_tile_iso(x_itr, y_itr, D_FULL);
- }
- }
-
- /* Then draw the tiles underneath to refresh the parts of them that
- * overlap onto the area just drawn. */
- put_tile_iso(x, y + height, D_TM_R); /* bottom side */
- for (i = 1; i < width; i++) {
- int x1 = x + i;
- int y1 = y + height;
- put_tile_iso(x1, y1, D_TM_R);
- put_tile_iso(x1, y1, D_T_L);
- }
-
- put_tile_iso(x + width, y, D_TM_L); /* right side */
- for (i=1; i < height; i++) {
- int x1 = x + width;
- int y1 = y + i;
- put_tile_iso(x1, y1, D_TM_L);
- put_tile_iso(x1, y1, D_T_R);
- }
-
- put_tile_iso(x + width, y + height, D_T_LR); /* right-bottom corner */
-
+ gui_rect_iterate(gui_x, gui_y, width, height, map_x, map_y, draw) {
+ put_tile_iso(map_x, map_y, draw);
+ } gui_rect_iterate_end;
/* Draw the goto lines on top of the whole thing. This is done last as
* we want it completely on top. */
- for (x_itr = x - 1; x_itr <= x + width; x_itr++) {
- for (y_itr = y - 1; y_itr <= y + height; y_itr++) {
- int x1 = x_itr;
- int y1 = y_itr;
- if (normalize_map_pos(&x1, &y1)) {
- adjc_dir_iterate(x1, y1, x2, y2, dir) {
- if (get_drawn(x1, y1, dir)) {
- draw_segment(x1, y1, dir);
- }
- } adjc_dir_iterate_end;
- }
+ gui_rect_iterate(gui_x, gui_y, width, height, x1, y1, draw) {
+ if (normalize_map_pos(&x1, &y1)) {
+ adjc_dir_iterate(x1, y1, x2, y2, dir) {
+ if (get_drawn(x1, y1, dir)) {
+ draw_segment(x1, y1, dir);
+ }
+ } adjc_dir_iterate_end;
}
- }
-
-
- /* Lastly draw our changes to the screen. */
- /* top left corner */
- map_to_canvas_pos(&canvas_start_x, &canvas_start_y, x, y);
-
- /* top left corner in isometric view */
- canvas_start_x -= height * NORMAL_TILE_WIDTH / 2;
-
- /* because of where get_canvas_xy() sets canvas_x */
- canvas_start_x += NORMAL_TILE_WIDTH / 2;
-
- /* And because units fill a little extra */
- canvas_start_y += NORMAL_TILE_HEIGHT - UNIT_TILE_HEIGHT;
-
- /* Here we draw a rectangle that includes the updated tiles. This
- * method can fail if the area wraps off one side of the screen and
- * back to the other. */
- dirty_rect(canvas_start_x, canvas_start_y,
- (height + width) * NORMAL_TILE_WIDTH / 2,
- (height + width) * NORMAL_TILE_HEIGHT / 2
- + NORMAL_TILE_HEIGHT / 2);
+ } gui_rect_iterate_end;
} else {
/* not isometric */
- int map_x, map_y;
-
- for (map_y = y; map_y < y + height; map_y++) {
- for (map_x = x; map_x < x + width; map_x++) {
- /*
- * We don't normalize until later because we want to draw
- * black tiles for unreal positions.
- */
- put_tile(map_x, map_y);
- }
- }
- /* Here we draw a rectangle that includes the updated tiles. This
- * method can fail if the area wraps off one side of the screen and
- * back to the other. */
- map_to_canvas_pos(&canvas_start_x, &canvas_start_y, x, y);
- dirty_rect(canvas_start_x, canvas_start_y,
- width * NORMAL_TILE_WIDTH,
- height * NORMAL_TILE_HEIGHT);
+ gui_rect_iterate(gui_x, gui_y, width, height, map_x, map_y, draw) {
+ /*
+ * We don't normalize until later because we want to draw
+ * black tiles for unreal positions.
+ */
+ put_tile(map_x, map_y);
+ } gui_rect_iterate_end;
}
- if (write_to_screen) {
- /* We never want a partial flush; that would leave the screen in an
- * inconsistent state. If the caller tells us to write_to_screen we
- * simply flush everything immediately. */
- flush_dirty();
- }
+ dirty_rect(canvas_x, canvas_y, width, height);
}
/**************************************************************************
@@ -1401,8 +1315,6 @@
**************************************************************************/
void update_map_canvas_visible(void)
{
- int map_x0, map_y0;
-
dirty_all();
/* Clear the entire mapview. This is necessary since if the mapview is
@@ -1414,22 +1326,7 @@
canvas_put_rectangle(mapview_canvas.store, COLOR_STD_BLACK,
0, 0, mapview_canvas.width, mapview_canvas.height);
- canvas_to_map_pos(&map_x0, &map_y0, 0, 0);
- if (is_isometric) {
- /* just find a big rectangle that includes the whole visible area. The
- invisible tiles will not be drawn. */
- int width, height;
-
- width = mapview_canvas.tile_width + mapview_canvas.tile_height + 2;
- height = width;
- update_map_canvas(map_x0 - 1, map_y0 - mapview_canvas.tile_width - 1,
- width, height, FALSE);
- } else {
- update_map_canvas(map_x0, map_y0,
- mapview_canvas.tile_width + 1,
- mapview_canvas.tile_height + 1,
- FALSE);
- }
+ update_map_canvas(0, 0, mapview_canvas.width, mapview_canvas.height);
show_city_descriptions();
}
@@ -1440,7 +1337,6 @@
void show_city_descriptions(void)
{
int canvas_x, canvas_y;
- int map_x0, map_y0;
if (!draw_city_names && !draw_city_productions) {
return;
@@ -1448,43 +1344,18 @@
prepare_show_city_descriptions();
- canvas_to_map_pos(&map_x0, &map_y0, 0, 0);
- if (is_isometric) {
- int w, h;
-
- for (h = -1; h < mapview_canvas.tile_height * 2; h++) {
- int x_base = map_x0 + h / 2 + (h != -1 ? h % 2 : 0);
- int y_base = map_y0 + h / 2 + (h == -1 ? -1 : 0);
-
- for (w = 0; w <= mapview_canvas.tile_width; w++) {
- int x = x_base + w;
- int y = y_base - w;
- struct city *pcity;
-
- if (normalize_map_pos(&x, &y)
- && (pcity = map_get_city(x, y))) {
- map_to_canvas_pos(&canvas_x, &canvas_y, x, y);
- show_city_desc(pcity, canvas_x, canvas_y);
- }
- }
- }
- } else { /* is_isometric */
- int x1, y1;
-
- for (x1 = 0; x1 <= mapview_canvas.tile_width; x1++) {
- for (y1 = 0; y1 <= mapview_canvas.tile_height; y1++) {
- int x = map_x0 + x1;
- int y = map_y0 + y1;
- struct city *pcity;
-
- if (normalize_map_pos(&x, &y)
- && (pcity = map_get_city(x, y))) {
- map_to_canvas_pos(&canvas_x, &canvas_y, x, y);
- show_city_desc(pcity, canvas_x, canvas_y);
- }
- }
+ gui_rect_iterate(mapview_canvas.gui_x0 - NORMAL_TILE_WIDTH,
+ mapview_canvas.gui_y0 - NORMAL_TILE_HEIGHT,
+ mapview_canvas.width + 2 * NORMAL_TILE_WIDTH,
+ mapview_canvas.height + NORMAL_TILE_HEIGHT, x, y, draw) {
+ struct city *pcity;
+
+ if (normalize_map_pos(&x, &y)
+ && (pcity = map_get_city(x, y))) {
+ map_to_canvas_pos(&canvas_x, &canvas_y, x, y);
+ show_city_desc(pcity, canvas_x, canvas_y);
}
- }
+ } gui_rect_iterate_end;
}
/****************************************************************************
Index: client/mapview_common.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.h,v
retrieving revision 1.58
diff -u -r1.58 mapview_common.h
--- client/mapview_common.h 23 Apr 2004 16:43:13 -0000 1.58
+++ client/mapview_common.h 24 Apr 2004 21:04:16 -0000
@@ -181,8 +181,7 @@
int canvas_x, int canvas_y,
enum draw_type draw, bool citymode);
-void update_map_canvas(int x, int y, int width, int height,
- bool write_to_screen);
+void update_map_canvas(int canvas_x, int canvas_y, int width, int height);
void update_map_canvas_visible(void);
void show_city_descriptions(void);
Index: client/packhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v
retrieving revision 1.362
diff -u -r1.362 packhand.c
--- client/packhand.c 14 Apr 2004 17:18:36 -0000 1.362
+++ client/packhand.c 24 Apr 2004 21:04:16 -0000
@@ -579,10 +579,7 @@
if ((draw_map_grid || draw_borders) && can_client_change_view()) {
/* We have to make sure we update any workers on the map grid, then
* redraw the city descriptions on top of them. */
- update_map_canvas(pcity->x - CITY_MAP_SIZE / 2,
- pcity->y - CITY_MAP_SIZE / 2,
- CITY_MAP_SIZE, CITY_MAP_SIZE, FALSE);
- queue_mapview_update(UPDATE_CITY_DESCRIPTIONS);
+ queue_mapview_update(UPDATE_MAP_CANVAS_VISIBLE);
} else {
refresh_tile_mapcanvas(pcity->x, pcity->y, FALSE);
}
|
|