[Freeciv-Dev] (PR#8299) wrapping GUI coordinates
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
<URL: http://rt.freeciv.org/Ticket/Display.html?id=8299 >
GUI coordinates need to wrap. That way when you scroll across using the
scrollbar you'll get the expected results.
There are two possible ways to do this: the easy way, and the hard way.
In the easy way we convert the GUI position to a map position, wrap that
map position, then convert it back to a GUI position. We have to
remember the GUI position's offset from the map position's tile so that
we end up with the proper result. Attached patch "A" does this. The
advantage is that it is easy. The disadvantage is that it's a hack and
won't be very extensible to other problems.
In the hard way we convert the GUI position to a "native" gui position.
The native position is aligned natively to the freeciv map, but
preserves pixel resolution so that each tile is of size
NORMAL_TILE_WIDHT*NORMAL_TILE_HEIGHT. We can then wrap this position
directly before converting it back to a gui position. Attached patch
"B" does this. The disadvantage is that this is hard: it takes more
code than the above method, the math is hard to follow, and the very
concept of "gui-native" coordinates is a bit bizarre. The advantage is
that it is a more flexible design: gui-native coordinates can be used to
do just about any topological operation needed on GUI coordinates while
preserving pixel resolution (for instance a gui_distance_vector()
function would be just a few lines).
I'm not sure which method is better.
jason
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.94
diff -u -r1.94 mapview_common.c
--- client/mapview_common.c 19 Mar 2004 20:22:07 -0000 1.94
+++ client/mapview_common.c 22 Mar 2004 18:05:09 -0000
@@ -324,12 +324,43 @@
}
/****************************************************************************
+ Normalize (wrap) the GUI position. This is equivalent to a map wrapping,
+ but in GUI coordinates so that pixel accuracy is preserved.
+****************************************************************************/
+static void normalize_gui_pos(int *gui_x, int *gui_y)
+{
+ int map_x, map_y, nat_x, nat_y, gui_x0, gui_y0, diff_x, diff_y;
+
+ gui_to_map_pos(&map_x, &map_y, *gui_x, *gui_y);
+ map_to_gui_pos(&gui_x0, &gui_y0, map_x, map_y);
+ diff_x = *gui_x - gui_x0;
+ diff_y = *gui_y - gui_y0;
+
+ /* Perform wrapping without any realness check. */
+ map_to_native_pos(&nat_x, &nat_y, map_x, map_y);
+ if (topo_has_flag(TF_WRAPX)) {
+ nat_x = FC_WRAP(nat_x, map.xsize);
+ }
+ if (topo_has_flag(TF_WRAPY)) {
+ nat_y = FC_WRAP(nat_y, map.ysize);
+ }
+ native_to_map_pos(&map_x, &map_y, nat_x, nat_y);
+
+ map_to_gui_pos(gui_x, gui_y, map_x, map_y);
+ *gui_x += diff_x;
+ *gui_y += diff_y;
+}
+
+/****************************************************************************
Change the mapview origin, clip it, and update everything.
****************************************************************************/
static void set_mapview_origin(int gui_x0, int gui_y0)
{
int xmin, xmax, ymin, ymax, xsize, ysize;
+ /* Normalize (wrap) the mapview origin. */
+ normalize_gui_pos(&gui_x0, &gui_y0);
+
/* First wrap/clip the position. Wrapping is done in native positions
* while clipping is done in scroll (native) positions. */
get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
@@ -404,12 +435,24 @@
*ymax += NORMAL_TILE_HEIGHT;
/* To be able to center on positions near the edges, we have to be
- * allowed to scroll past those edges. */
+ * allowed to scroll all the way to those edges. To allow wrapping the
+ * clipping boundary needs to extend past the edge - a half-tile in
+ * iso-view or a full tile in non-iso view. The above math already has
+ * taken care of some of this so all that's left is to fix the corner
+ * cases. */
if (topo_has_flag(TF_WRAPX)) {
*xmax += *xsize;
+ if (!is_isometric) {
+ /* In non-iso view we need to be able to scroll a little further to
+ * the left. */
+ *xmin -= NORMAL_TILE_WIDTH;
+ }
}
if (topo_has_flag(TF_WRAPY)) {
*ymax += *ysize;
+ if (!is_isometric) {
+ *ymin -= NORMAL_TILE_HEIGHT;
+ }
}
} else {
/* Otherwise it's hard. Very hard. Impossible, in fact. This is just
Index: client/mapview_common.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/mapview_common.c,v
retrieving revision 1.94
diff -u -r1.94 mapview_common.c
--- client/mapview_common.c 19 Mar 2004 20:22:07 -0000 1.94
+++ client/mapview_common.c 22 Mar 2004 18:44:20 -0000
@@ -324,12 +324,104 @@
}
/****************************************************************************
+ A "gui" "native" coordinate system is oriented the same way as the map,
+ but preserves pixel granularity for the canvas. Each tile is of size
+ NORMAL_TILE_WIDTH * NORMAL_TILE_HEIGHT in each direction.
+****************************************************************************/
+static void gui_to_guinat_pos(int *guinat_x, int *guinat_y,
+ int gui_x, int gui_y)
+{
+ const int W = NORMAL_TILE_WIDTH, H = NORMAL_TILE_HEIGHT;
+ int guimap_x, guimap_y;
+
+ /* This echoes the conversion in gui_to_map_pos, except we don't lose
+ * pixel granularity. We transorm GUI tiles into square tiles with
+ * side W * H. */
+ if (is_isometric) {
+ guimap_x = gui_x * H + gui_y * W;
+ guimap_y = gui_y * W - gui_x * H;
+ } else {
+ guimap_x = gui_x * H;
+ guimap_y = gui_y * W;
+ }
+
+ /* Now convert into a "gui" native position. This echoes the conversion
+ * in map_to_native_pos, except again we keep pixel granularity. */
+ if (topo_has_flag(TF_ISO)) {
+ *guinat_y = guimap_x + guimap_y - map.xsize * W * H;
+ *guinat_x = (2 * guimap_x - *guinat_y - (*guinat_y & 1)) / 2;
+ } else {
+ *guinat_x = guimap_x;
+ *guinat_y = guimap_y;
+ }
+}
+
+/****************************************************************************
+ A "gui" "native" coordinate system is oriented the same way as the map,
+ but preserves pixel granularity for the canvas. Each tile is of size
+ NORMAL_TILE_WIDTH * NORMAL_TILE_HEIGHT in each direction.
+****************************************************************************/
+static void guinat_to_gui_pos(int *gui_x, int *gui_y,
+ int guinat_x, int guinat_y)
+{
+ const int W = NORMAL_TILE_WIDTH, H = NORMAL_TILE_HEIGHT;
+ int guimap_x, guimap_y;
+
+ /* Convert the "gui-native" position into a "gui-map" position. This
+ * echoes the conversion in native_to_map_pos, except the scale is
+ * larger. */;
+ if (topo_has_flag(TF_ISO)) {
+ guimap_x = (guinat_y + (guinat_y & 1)) / 2 + guinat_x;
+ guimap_y = guinat_y - guimap_x + map.xsize * W * H;
+ } else {
+ guimap_x = guinat_x;
+ guimap_y = guinat_y;
+ }
+
+ /* Now convert directly into a GUI position. This echoes the conversion
+ * in map_to_gui_pos, except the scale is larger. */
+ if (is_isometric) {
+ *gui_x = (guimap_x - guimap_y) / (H * 2);
+ *gui_y = (guimap_x + guimap_y) / (W * 2);
+ } else {
+ *gui_x = guimap_x / H;
+ *gui_y = guimap_y / W;
+ }
+}
+
+/****************************************************************************
+ Normalize (wrap) the GUI position. This is equivalent to a map wrapping,
+ but in GUI coordinates so that pixel accuracy is preserved.
+****************************************************************************/
+static void normalize_gui_pos(int *gui_x, int *gui_y)
+{
+ const int W = NORMAL_TILE_WIDTH, H = NORMAL_TILE_HEIGHT;
+ int guinat_x, guinat_y;
+
+ gui_to_guinat_pos(&guinat_x, &guinat_y, *gui_x, *gui_y);
+
+ /* Now wrap. */
+ if (topo_has_flag(TF_WRAPX)) {
+ guinat_x = FC_WRAP(guinat_x, map.xsize * W * H);
+ }
+ if (topo_has_flag(TF_WRAPY)) {
+ guinat_y = FC_WRAP(guinat_y, map.ysize * W * H);
+ }
+
+ /* Convert back to "gui" map position. */
+ guinat_to_gui_pos(gui_x, gui_y, guinat_x, guinat_y);
+}
+
+/****************************************************************************
Change the mapview origin, clip it, and update everything.
****************************************************************************/
static void set_mapview_origin(int gui_x0, int gui_y0)
{
int xmin, xmax, ymin, ymax, xsize, ysize;
+ /* Normalize (wrap) the mapview origin. */
+ normalize_gui_pos(&gui_x0, &gui_y0);
+
/* First wrap/clip the position. Wrapping is done in native positions
* while clipping is done in scroll (native) positions. */
get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
@@ -404,12 +496,20 @@
*ymax += NORMAL_TILE_HEIGHT;
/* To be able to center on positions near the edges, we have to be
- * allowed to scroll past those edges. */
+ * allowed to scroll all the way to those edges. To allow wrapping the
+ * clipping boundary needs to extend past the edge - a half-tile in
+ * iso-view or a full tile in non-iso view. The above math already has
+ * taken care of some of this so all that's left is to fix the corner
+ * cases. */
if (topo_has_flag(TF_WRAPX)) {
*xmax += *xsize;
+
+ /* We need to be able to scroll a little further to the left. */
+ *xmin -= NORMAL_TILE_WIDTH;
}
if (topo_has_flag(TF_WRAPY)) {
*ymax += *ysize;
+ *ymin -= NORMAL_TILE_HEIGHT;
}
} else {
/* Otherwise it's hard. Very hard. Impossible, in fact. This is just
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Freeciv-Dev] (PR#8299) wrapping GUI coordinates,
Jason Short <=
|
|