Skip to content

Commit

Permalink
Add documentation for new coord_point
Browse files Browse the repository at this point in the history
To help people understand the game's coordinate systems and how to use
the new point types, add some documentation

Move the description of coordinate systems out of
coordinate_conversions.h into a markdown file, and reference that new
file from coordinate_conversions.h and coordinates.h.

(cherry picked from commit c1cabb8fa26c5cf4c1e1a65422ab85be3d9cafc7)
  • Loading branch information
jbytheway authored and olanti-p committed Sep 27, 2021
1 parent 7e1a2e2 commit a17136f
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 35 deletions.
207 changes: 207 additions & 0 deletions doc/POINTS_COORDINATES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# Points, tripoints, and coordinate systems

## Axes

The game is three-dimensional, with the axes oriented as follows:
* The **x-axis** goes from left to right across the display (in non-isometric
views).
* The **y-axis** goes from top to bottom of the display.
* The **z-axis** is vertical, with negative z pointing underground and positive
z pointing to the sky.

## Coordinate systems

CDDA uses a variety of coordinate systems for different purposes. These differ
by scale and origin.

The most precise coordinates are **map square** (ms) coordinates. These refer to
the tiles you see normally when playing the game.

Two origins for map square coordinates are common:
* **Absolute** coordinates, sometimes called global, which are a global system
for the whole game, relative to a fixed origin.
* **Local** coordinates, which are relative to the corner of the current "reality
bubble", or `map` roughly centered on the avatar. In local map square
coordinates, `x` and `y` values will both fall in the range [0,120).

The next scale is **submap** (sm) coordinates. One submap is 12x12
(`SEEX`x`SEEY`) map squares. Submaps are the scale at which chunks of the map
are loaded or saved as they enter or leave the reality bubble.

Next comes **overmap terrain** (omt) coordinates. One overmap terrain is 2x2
submaps. Overmap terrains correspond to a single tile on the map view in-game,
and are the scale of chunk of mapgen.

Largest are **overmap** (om) coordinates. One overmap is 180x180
(`OMAPX`x`OMAPY`) overmap terrains. Large-scale mapgen (e.g. city layout)
happens one overmap at a time.

Lastly, these is a system called **segment** (seg) coordinates. These are only
used in saving/loading submaps and you are unlikely to encounter them.

As well as absolute and local coordinates, sometimes we need to use coordinates
relative so some larger scale. For example, when performing mapgen for a
single overmap, we want to work with coordinates within that overmap. This
will be an overmap terrain-scale point relative to the corner of its containing
overmap, and so typically take `x` and `y` values in the range [0,180).

## Vertical coordinates

Although `x` and `y` coordinates work at all these various scales, `z`
coordinates are consistent across all contexts. They lie in the range
[-`OVERMAP_DEPTH`,`OVERMAP_HEIGHT`].

## Vehicle coordinates

Each vehicle has its own origin point, which will be at a particular part of
the vehicle (e.g. it might be at the driver's seat). The origin can move if
the vehicle is damaged and all the vehicle parts at that location are
destroyed.

Vehicles use two systems of coordinates relative to their origin:

* **mount** coordinates provide a location for vehicle parts that does not
change as the vehicle moves. It is the map square of that part, relative to
the vehicle origin, when the vehicle is facing due east.

* **map square** is the map square, relative to the origin, but accounting for
the vehicle's current facing.

Vehicle facing is implemented via a combination of rotations (by quarter turns)
and shearing to interpolate between quarter turns. The logic to convert
between vehicle mount and map square coordinates is complicated and handled by
the `vehicle::coord_translate()` and `vehicle::mount_to_tripoint()` families of
functions.

Currently, vehicle mount coordinates do not have a z-level component, but
vehicle map square coordinates do. The z coordinate is relative to the vehicle
origin.

## Point types

To work with these coordinate systems we have a variety of types. These are
defined in [`coordinates.h`](../src/coordinates.h). For example, we have
`point_abs_ms` for absolute map-square coordinates. The three parts of the
type name are *dimension*`_`*origin*`_`*scale*.

* **dimension** is either `point` for two-dimensional or `tripoint` for
three-dimensional.
* **origin** specifies what the value is relative to, and can be:
* `rel` means relative to some arbitrary point. This is the result of
subtracting two points with a common origin. It would be used for example
to represent the offset between the avatar and a monster they are shooting
at.
* `abs` means global absolute coordinates.
* `sm` means relative to a corner of a submap.
* `omt` means relative to a corner of an overmap terrain.
* `om` means relative to a corner of an overmap.
* `veh` means relative to a vehicle origin.
* **scale** means the scale as discussed above.
* `ms` for map square.
* `sm` for submap.
* `omt` for overmap terrain.
* `seg` for segment.
* `om` for overmap.
* `mnt` for vehicle mount coordinates (only relevant for the `veh` origin).

## Raw point types

As well as these types with origin and scale encoded into the type, there are
simple raw point types called just `point` and `tripoint`. These can be used
when no particular game scale is intended.

At time of writing we are still in the process of transitioning the codebase
away from using these raw point types everywhere, so you are likely to see
legacy code using them in places where the more type-safe points might seem
appropriate.

New code should prefer to use the types which include their coordinate system
where feasible.

## Converting between point types

### Changing scale

To change the scale of a point without changing its origin, use `project_to`.
For example:

```c++
point_abs_ms pos_ms = get_avatar()->global_square_location().xy();
point_abs_omt pos_omt = project_to<coords::omt>( pos_ms );
assert( pos_omt == get_avatar()->global_omt_location().xy() );
```
The same function `project_to` can be used for scaling up or down. When
converting to a coarser coordinate system precision is of course lost. If you
care about the remainder then you must instead use `project_remain`.
`project_remain` allows you to convert to a coarser coordinate system and also
capture the remainder relative to that coarser point. It returns a helper
struct intended to be used with
[`std::tie`](https://en.cppreference.com/w/cpp/utility/tuple/tie) to capture
the two parts of the result. For example, suppose you want to know which
overmap the avatar is in, and which overmap terrain they are in within that
overmap.
```c++
point_abs_omt abs_pos = get_avatar()->global_omt_location().xy();
point_abs_om overmap;
point_om_omt omt_within_overmap;
std::tie( overmap, omt_within_overmap ) = project_remain<coords::om>( abs_pos );
```

That makes sense for two-dimensional `point` types, but how does it handle
`tripoint`? Recall that the z-coordinates do not scale along with the
horizontal dimensions, so `z` values are unchanged by `project_to` and
`project_remain`. However, for `project_remain` we don't want to duplicate the
z-coordinate in both parts of the result, so you must choose exactly one to be
a `tripoint`. In the example above, z-coodinates do not have much meaning at
the overmap scale, so you probably want the z-coordinate in
`omt_within_overmap`. Than can be done as follows:

```c++
tripoint_abs_omt abs_pos = get_avatar()->global_omt_location();
point_abs_om overmap;
tripoint_om_omt omt_within_overmap;
std::tie( overmap, omt_within_overmap ) = project_remain<coords::om>( abs_pos );
```
The last available operation for rescaling points is `project_combine`. This
performs the opposite operation from `project_remain`. Given two points where
the origin of the second matches the scale of the first, you can combine them
into a single value. As you might expect from the above discussion, one of
these two can be a `tripoint`, but not both.
```c++
tripoint_abs_omt abs_pos = get_avatar()->global_omt_location();
point_abs_om overmap;
tripoint_om_omt omt_within_overmap;
std::tie( overmap, omt_within_overmap ) = project_remain<coords::om>( abs_pos );
tripoint_abs_omt abs_pos_again = project_combine( overmap, omt_within_overmap );
assert( abs_pos == abs_pos_again );
```

### Changing origin

`project_remain` and `project_combine` facilitate some changes of origin, but
only those origins specifically related to rescaling. To convert to or from
local or vehicle coordinates requires a specific `map` or `vehicle` object.

TODO: write some examples once this is implemented.

## Point operations

We provide standard arithmetic operations as overloaded operators, but limit
them to prevent bugs. For example, most point types cannot be multiplied by a
constant, but ones with the `rel` origin can (it makes sense to say "half as
far in the same direction").

Similarly, you can't generally add two points together, but you can when one of
them has the `rel` origin, or if one of them is a raw point type.

For computing distances a variety of functions are available, depending on your
requirements: `square_dist`, `trig_dist`, `rl_dist`, `manhattan_dist`. Other
related utility functions include `direction_from` and `line_to`.

To iterate over nearby points of the same type you can use
`closest_points_first`.
38 changes: 7 additions & 31 deletions src/coordinate_conversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,13 @@
#include "point.h"

/**
* Coordinate systems used here are:
* overmap (om): the position of an overmap. Each overmap stores
* this as overmap::loc (access with overmap::pos()).
* There is a unique overmap for each overmap coordinate.
*
* segment (seg): A segment is a unit of terrain saved to a directory.
* Each segment contains SEG_SIZExSEG_SIZE overmap terrains, and is used only for
* saving/loading submaps, see mapbuffer.cpp.
* Translation from omt to seg:
* om.x /= SEG_SIZE
* om.y /= SEG_SIZE
* (with special handling for negative values).
* This file defines legacy coordinate conversion functions. We should be
* migrating to the new functions defined in coordinates.h.
*
* For documentation on coordinate systems in general see
* doc/POINTS_COORDINATES.md.
*
* TODO: type for mmr coords, also add them to doc/POINTS_COORDINATES.md
* memory map region (mmr): Memory map region is a unit of tile memory saved to a directory.
* Each region contains MM_REG_SIZExMM_REG_SIZE memorized submaps, and is used only for
* saving/loading memorized submaps, see map_memory.cpp.
Expand All @@ -26,25 +20,7 @@
* sm.y /= MM_REG_SIZE
* (with special handling for negative values).
*
* overmap terrain (omt): the position of a overmap terrain (oter_id).
* Each overmap contains (OMAPX * OMAPY) overmap terrains.
* Translation from omt to om:
* om.x /= OMAPX
* om.y /= OMAPY
* (with special handling for negative values).
*
* Z-components are never considered and simply copied.
*
* submap (sm): each overmap terrain contains (2*2) submaps.
* Translating from sm to omt coordinates:
* sm.x /= 2
* sm.y /= 2
*
* map square (ms): used by @ref map, each map square may contain a single
* piece of furniture, it has a terrain (ter_t).
* There are SEEX*SEEY map squares in each submap.
*
* The class provides static translation functions, named like this:
* This file provides static translation functions, named like this:
@code
static point <from>_to_<to>_copy(int x, int y);
static point <from>_to_<to>_copy(const point& p);
Expand Down
19 changes: 15 additions & 4 deletions src/coordinates.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,19 @@ constexpr origin origin_from_scale( scale s )
}
}

// A generic coordinate-type-safe point.
// Point should be the underlying representation type (either point or
// tripoint).
// Scale and Origin define the coordinate system for the point.
/**
* A generic coordinate-type-safe point.
*
* Generally you wouldn't use this class directly, but via the typedefs below
* (point_abs_ms, etc.).
*
* Point should be the underlying representation type (either point or
* tripoint).
*
* Origin and Scale define the coordinate system for the point.
*
* For more details see doc/POINTS_COORDINATES.md.
*/
template<typename Point, origin Origin, scale Scale>
class coord_point
{
Expand Down Expand Up @@ -436,6 +445,8 @@ struct hash<coords::coord_point<Point, Origin, Scale>> {
* For example:
* point_omt_ms is the position of a map square within an overmap terrain.
* tripoint_rel_sm is a relative tripoint submap offset.
*
* For more details see doc/POINTS_COORDINATES.md.
*/
/*@{*/
using point_rel_ms = coords::coord_point<point, coords::origin::relative, coords::ms>;
Expand Down

0 comments on commit a17136f

Please sign in to comment.