Complete.Org: Mailing Lists: Archives: freeciv-dev: March 2004:
[Freeciv-Dev] Re: (PR#8299) wrapping GUI coordinates
Home

[Freeciv-Dev] Re: (PR#8299) wrapping GUI coordinates

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: jdorje@xxxxxxxxxxxxxxxxxxxxx
Subject: [Freeciv-Dev] Re: (PR#8299) wrapping GUI coordinates
From: "rwetmore@xxxxxxxxxxxx" <rwetmore@xxxxxxxxxxxx>
Date: Wed, 24 Mar 2004 22:31:24 -0800
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=8299 >


Jason Short wrote:
> <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.

GUI views need to scroll smoothly across wrapping boundaries.
GUI coordinates may not need to wrap to do this.

Afterall the basic GUI window is a Flat-Earth widget that (can) floats
over a map surface to handle scrolling.

> There are two possible ways to do this: the easy way, and the hard way.

And *always* the third way not yet considered ...

> 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.

It is correct.
It leaves wrapping in the native space.
It separates the intrinsically tile granular wrapping from any weird
and wonderful GUI backing store to window mappings that might involve
varying scaling and/or pixel resolutions.
It is likely to be the most extensible for doing odd GUI stuff, and
the most robust (single source wrapping code never touched).

> In the hard way we convert the GUI position to a "native" gui position. 

Converts a 1xn problem set to an nxn one. But may have value in some
hard to handle special cases.

>   The native position is aligned natively to the freeciv map, but 
> preserves pixel resolution so that each tile is of size 

Valid, clean extension, but tricky ramifications in the general case.
Should be examined carefully. It might work if done right.

> 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.  

GUI-native is not bizarre, but may be unneeded and more complicated than
is worth the trouble. Prototyping and experimenting is *good* (TM) way
to explore issues, though.

> 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).

It is a wide open/not-yet-thought-through design/hack that has not been
consolidated to the point where it obeys rules like the current native
wrapping that all use. Using a backing store and GUI shifted window as
above is probably so simple to get the scrolling right in two steps that
a one shot hack is probably not worth it.

> I'm not sure which method is better.

I'd like to understand a bit more about what the goal actually is and
the hard technical problems this causes before trying to answer that.

> 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]