[Freeciv-Dev] (PR#12874) zigzagging of the overview viewrect
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=12874 >
As anyone who plays in iso-view knows the overview has a tendancy to
zig-zag when you scroll. Very ugly.
This is caused because all calculations are clipped to the nearest tile.
There are two operations affected by this. The first is the overview
centering (wrapping the overview to keep the viewrect centered); we
take the center tile of the mapcanvas and find the overview position for
it. The second is the drawing of the viewrect itself, where we take the
tile coordinates of the four corners of the mapview and convert those to
overview positions. In both cases there are problems because as the
mapview slides upward (for instance) the tiles being looked at may
themselves zig-zag a bit.
The solution is to avoid the clipping. PR#8299 had a method to do this
using gui-native coordinates; very complicated. But we can also do this
just by using floating-point values for the coordinates. This patch
introduces a gui_to_overview_pos function that does this whole
conversion without clipping at any stage. There's also a
gui_to_natural_pos function that just does the conversion into natural
positions.
The former function is used in the calculation of the viewrect position,
thus preventing zigzagging of the viewrect. get_mapview_corners (an
even uglier hack to do the same thing) is no longer needed and is removed.
The latter function is used in the centering of the overview. The
overview origin is still stored using natural positions but now it uses
a floating-point value to have good precision here.
All in all this should give nearly perfect results. One remaining flaw
is that the calculation of the viewrect corners fails if the mapview is
larger than the map.
-jason
? data/mom
? data/mom.serv
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.227
diff -u -r1.227 mapview_common.c
--- client/mapview_common.c 20 Apr 2005 16:44:53 -0000 1.227
+++ client/mapview_common.c 23 Apr 2005 02:16:10 -0000
@@ -471,7 +471,6 @@
const int width = mapview.width, height = mapview.height;
int common_x0, common_x1, common_y0, common_y1;
int update_x0, update_x1, update_y0, update_y1;
- struct tile *map_center;
/* Then update everything. This does some tricky math to avoid having
* to do unnecessary redraws in update_map_canvas. This makes for ugly
@@ -544,8 +543,7 @@
update_map_canvas(0, 0, mapview.store_width, mapview.store_height);
}
- map_center = get_center_tile_mapcanvas();
- center_tile_overviewcanvas(map_center);
+ center_tile_overviewcanvas();
if (hover_state == HOVER_GOTO || hover_state == HOVER_PATROL) {
create_line_at_mouse_pos();
}
@@ -2145,71 +2143,6 @@
}
/**************************************************************************
- Find the corners of the mapview, in overview coordinates. Used to draw
- the "mapview window" rectangle onto the overview.
-**************************************************************************/
-void get_mapview_corners(int x[4], int y[4])
-{
- int map_x0, map_y0;
-
- base_canvas_to_map_pos(&map_x0, &map_y0, 0, 0);
- map_to_overview_pos(&x[0], &y[0], map_x0, map_y0);
-
- /* Note: these calculations operate on overview coordinates as if they
- * are natural. Corners may be off by one tile, however. */
-
- if (tileset_is_isometric(tileset) && !MAP_IS_ISOMETRIC) {
- /* We start with the west corner. */
-
- /* North */
- x[1] = x[0] + OVERVIEW_TILE_WIDTH * mapview.tile_width;
- y[1] = y[0] - OVERVIEW_TILE_HEIGHT * mapview.tile_width;
-
- /* East */
- x[2] = x[1] + OVERVIEW_TILE_WIDTH * mapview.tile_height;
- y[2] = y[1] + OVERVIEW_TILE_HEIGHT * mapview.tile_height;
-
- /* South */
- x[3] = x[0] + OVERVIEW_TILE_WIDTH * mapview.tile_height;
- y[3] = y[0] + OVERVIEW_TILE_HEIGHT * mapview.tile_height;
- } else if (!tileset_is_isometric(tileset) && MAP_IS_ISOMETRIC) {
- /* We start with the west corner. Note the X scale is smaller. */
-
- /* North */
- x[1] = x[0] + OVERVIEW_TILE_WIDTH * mapview.tile_width / 2;
- y[1] = y[0] + OVERVIEW_TILE_HEIGHT * mapview.tile_width;
-
- /* East */
- x[2] = x[1] - OVERVIEW_TILE_WIDTH * mapview.tile_height / 2;
- y[2] = y[1] + OVERVIEW_TILE_HEIGHT * mapview.tile_height;
-
- /* South */
- x[3] = x[2] - OVERVIEW_TILE_WIDTH * mapview.tile_width / 2;
- y[3] = y[2] - OVERVIEW_TILE_HEIGHT * mapview.tile_width;
- } else {
- /* We start with the northwest corner. */
- int screen_width = mapview.tile_width;
- int screen_height = mapview.tile_height * (tileset_is_isometric(tileset)
- ? 2 : 1);
-
- /* Northeast */
- x[1] = x[0] + OVERVIEW_TILE_WIDTH * screen_width - 1;
- y[1] = y[0];
-
- /* Southeast */
- x[2] = x[1];
- y[2] = y[0] + OVERVIEW_TILE_HEIGHT * screen_height - 1;
-
- /* Southwest */
- x[3] = x[0];
- y[3] = y[2];
- }
-
- freelog(LOG_DEBUG, "(%d,%d)->(%d,%x)->(%d,%d)->(%d,%d)",
- x[0], y[0], x[1], y[1], x[2], y[2], x[3], y[3]);
-}
-
-/**************************************************************************
Returns TRUE if cached drawing is possible. If the mapview is too large
we have to turn it off.
**************************************************************************/
@@ -2348,7 +2281,7 @@
if (map_exists() && can_client_change_view()) {
if (tile_size_changed) {
update_map_canvas_visible();
- center_tile_overviewcanvas(get_center_tile_mapcanvas());
+ center_tile_overviewcanvas();
unqueue_mapview_updates(TRUE);
redrawn = TRUE;
}
Index: client/mapview_common.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.h,v
retrieving revision 1.111
diff -u -r1.111 mapview_common.h
--- client/mapview_common.h 31 Mar 2005 17:28:02 -0000 1.111
+++ client/mapview_common.h 23 Apr 2005 02:16:10 -0000
@@ -226,8 +226,6 @@
struct tile *canvas_pos_to_tile(int canvas_x, int canvas_y);
struct tile *canvas_pos_to_nearest_tile(int canvas_x, int canvas_y);
-void get_mapview_corners(int x[4], int y[4]);
-
void get_mapview_scroll_window(int *xmin, int *ymin,
int *xmax, int *ymax,
int *xsize, int *ysize);
Index: client/overview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/overview_common.c,v
retrieving revision 1.3
diff -u -r1.3 overview_common.c
--- client/overview_common.c 23 Apr 2005 01:36:46 -0000 1.3
+++ client/overview_common.c 23 Apr 2005 02:16:10 -0000
@@ -35,6 +35,69 @@
static bool overview_dirty = FALSE;
/****************************************************************************
+ Translate from gui to natural coordinate systems. This provides natural
+ coordinates as a floating-point value so there is no loss of information
+ in the resulting values.
+****************************************************************************/
+static void gui_to_natural_pos(const struct tileset *t,
+ double *ntl_x, double *ntl_y,
+ int gui_x, int gui_y)
+{
+ const double gui_xd = gui_x, gui_yd = gui_y;
+ const double W = tileset_tile_width(t), H = tileset_tile_height(t);
+ double map_x, map_y;
+
+ /* First convert to map positions. This ignores hex conversions; we're
+ * not looking for an exact tile. */
+ if (tileset_is_isometric(t)) {
+ /* Includes hex cases. */
+ map_x = (gui_xd * H + gui_yd * W) / (W * H);
+ map_y = (gui_yd * W - gui_xd * H) / (W * H);
+ } else {
+ map_x = gui_xd / W;
+ map_y = gui_yd / H;
+ }
+
+ /* Now convert to natural positions. Note this assumes the macro form
+ * of the conversion will work with floating-point values. */
+ MAP_TO_NATURAL_POS(ntl_x, ntl_y, map_x, map_y);
+
+}
+
+/****************************************************************************
+ Translate from gui to overview coordinate systems.
+****************************************************************************/
+static void gui_to_overview_pos(const struct tileset *t,
+ int *ovr_x, int *ovr_y,
+ int gui_x, int gui_y)
+{
+ double ntl_x, ntl_y;
+
+ gui_to_natural_pos(t, &ntl_x, &ntl_y, gui_x, gui_y);
+
+ /* Now convert straight to overview coordinates. */
+ *ovr_x = floor((ntl_x - (double)overview.map_x0) * OVERVIEW_TILE_SIZE);
+ *ovr_y = floor((ntl_y - (double)overview.map_y0) * OVERVIEW_TILE_SIZE);
+
+ /* Now do additional adjustments. See map_to_overview_pos(). */
+ if (topo_has_flag(TF_WRAPX)) {
+ *ovr_x = FC_WRAP(*ovr_x, NATURAL_WIDTH * OVERVIEW_TILE_SIZE);
+ } else {
+ if (MAP_IS_ISOMETRIC) {
+ /* HACK: For iso-maps that don't wrap in the X direction we clip
+ * a half-tile off of the left and right of the overview. This
+ * means some tiles only are halfway shown. However it means we
+ * don't show any unreal tiles, which we'd otherwise be doing. The
+ * rest of the code can't handle unreal tiles in the overview. */
+ *ovr_x -= OVERVIEW_TILE_SIZE;
+ }
+ }
+ if (topo_has_flag(TF_WRAPY)) {
+ *ovr_y = FC_WRAP(*ovr_y, NATURAL_HEIGHT * OVERVIEW_TILE_SIZE);
+ }
+}
+
+/****************************************************************************
Return color for overview map tile.
****************************************************************************/
static enum color_std overview_tile_color(struct tile *ptile)
@@ -78,6 +141,7 @@
static void redraw_overview(void)
{
struct canvas *dest = get_overview_window();
+ int i, x[4], y[4];
if (!dest || !overview.map) {
return;
@@ -96,21 +160,24 @@
canvas_copy(dst, src, x, y, 0, 0, ix, iy);
}
- {
- int i;
- int x[4], y[4];
-
- get_mapview_corners(x, y);
-
- for (i = 0; i < 4; i++) {
- int src_x = x[i];
- int src_y = y[i];
- int dst_x = x[(i + 1) % 4];
- int dst_y = y[(i + 1) % 4];
+ gui_to_overview_pos(tileset, &x[0], &y[0],
+ mapview.gui_x0, mapview.gui_y0);
+ gui_to_overview_pos(tileset, &x[1], &y[1],
+ mapview.gui_x0 + mapview.width, mapview.gui_y0);
+ gui_to_overview_pos(tileset, &x[2], &y[2],
+ mapview.gui_x0 + mapview.width,
+ mapview.gui_y0 + mapview.height);
+ gui_to_overview_pos(tileset, &x[3], &y[3],
+ mapview.gui_x0, mapview.gui_y0 + mapview.height);
+
+ for (i = 0; i < 4; i++) {
+ int src_x = x[i];
+ int src_y = y[i];
+ int dst_x = x[(i + 1) % 4];
+ int dst_y = y[(i + 1) % 4];
- canvas_put_line(overview.window, COLOR_STD_WHITE, LINE_NORMAL,
- src_x, src_y, dst_x - src_x, dst_y - src_y);
- }
+ canvas_put_line(overview.window, COLOR_STD_WHITE, LINE_NORMAL,
+ src_x, src_y, dst_x - src_x, dst_y - src_y);
}
canvas_copy(dest, overview.window,
@@ -139,29 +206,48 @@
}
}
+/****************************************************************************
+ Equivalent to FC_WRAP, but it works for doubles.
+****************************************************************************/
+static double wrap_double(double value, double wrap)
+{
+ while (value < 0) {
+ value += wrap;
+ }
+ while (value >= wrap) {
+ value -= wrap;
+ }
+ return value;
+}
+
/**************************************************************************
Center the overview around the mapview.
**************************************************************************/
-void center_tile_overviewcanvas(struct tile *ptile)
+void center_tile_overviewcanvas(void)
{
- /* The overview coordinates are equivalent to (scaled) natural
- * coordinates. */
- do_in_natural_pos(ntl_x, ntl_y, ptile->x, ptile->y) {
- /* NOTE: this embeds the map wrapping in the overview code. This is
- * basically necessary for the overview to be efficiently
- * updated. */
- if (topo_has_flag(TF_WRAPX)) {
- overview.map_x0 = FC_WRAP(ntl_x - NATURAL_WIDTH / 2, NATURAL_WIDTH);
- } else {
- overview.map_x0 = 0;
- }
- if (topo_has_flag(TF_WRAPY)) {
- overview.map_y0 = FC_WRAP(ntl_y - NATURAL_HEIGHT / 2, NATURAL_HEIGHT);
- } else {
- overview.map_y0 = 0;
- }
- redraw_overview();
- } do_in_natural_pos_end;
+ double ntl_x, ntl_y;
+
+ gui_to_natural_pos(tileset, &ntl_x, &ntl_y,
+ mapview.gui_x0 + mapview.width / 2,
+ mapview.gui_y0 + mapview.height / 2);
+
+ /* NOTE: this embeds the map wrapping in the overview code. This is
+ * basically necessary for the overview to be efficiently
+ * updated. */
+ if (topo_has_flag(TF_WRAPX)) {
+ overview.map_x0 = wrap_double(ntl_x - (double)NATURAL_WIDTH / 2.0,
+ NATURAL_WIDTH);
+ } else {
+ overview.map_x0 = 0;
+ }
+ if (topo_has_flag(TF_WRAPY)) {
+ overview.map_y0 = wrap_double(ntl_y - (double)NATURAL_HEIGHT / 2.0,
+ NATURAL_HEIGHT);
+ } else {
+ overview.map_y0 = 0;
+ }
+
+ redraw_overview();
}
/**************************************************************************
Index: client/overview_common.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/overview_common.h,v
retrieving revision 1.3
diff -u -r1.3 overview_common.h
--- client/overview_common.h 23 Apr 2005 01:36:46 -0000 1.3
+++ client/overview_common.h 23 Apr 2005 02:16:10 -0000
@@ -21,7 +21,7 @@
/* Holds all information about the overview aka minimap. */
struct overview {
/* The following fields are controlled by mapview_common.c. */
- int map_x0, map_y0;
+ double map_x0, map_y0; /* Origin of the overview, in natural coords. */
int width, height; /* Size in pixels. */
/* Holds the map, unwrapped. */
@@ -50,7 +50,7 @@
void overview_update_tile(struct tile *ptile);
void calculate_overview_dimensions(void);
-void center_tile_overviewcanvas(struct tile *ptile);
+void center_tile_overviewcanvas(void);
void flush_dirty_overview(void);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Freeciv-Dev] (PR#12874) zigzagging of the overview viewrect,
Jason Short <=
|
|