docs: "The Affine Space" chapter updated with the latest changes

This commit is contained in:
Mateusz Pusz
2023-08-23 15:43:19 +02:00
parent 5af82e3162
commit d8f646a1e4

View File

@@ -26,6 +26,7 @@ Here are the primary operations one can do in the affine space:
- _vector_ / scalar -> _vector_ - _vector_ / scalar -> _vector_
- _point_ - _point_ -> _vector_ - _point_ - _point_ -> _vector_
- _point_ + _vector_ -> _point_ - _point_ + _vector_ -> _point_
- _vector_ + _point_ -> _point_
- _point_ - _vector_ -> _point_ - _point_ - _vector_ -> _point_
!!! note !!! note
@@ -50,95 +51,76 @@ As we already know, a `quantity` type provides all operations required for _vect
the affine space. the affine space.
## _Point_ is modeled by `quantity_point` ## _Point_ is modeled by `PointOrigin` and `quantity_point`
A _point_ is an absolute quantity with respect to an origin and is represented in the library with a In the **mp-units** library the _point_ abstraction is modelled by:
`quantity_point` class template:
- [`PointOrigin` concept](basic_concepts.md#PointOrigin) that specifies measurement origin,
- `quantity_point` class template that specifies a _point_ relative to a specific predefined origin.
### Absolute _point_ origin
The **absolute point origin** specifies where the "zero" of our measurement's scale is. User can
specify such an origin by deriving from the `absolute_point_origin` class template:
```cpp
constexpr struct mean_sea_level : absolute_point_origin<isq::altitude> {} mean_sea_level;
```
### `quantity_point`
The `quantity_point` class template specifies an absolute quantity with respect to an origin:
```cpp ```cpp
template<Reference auto R, template<Reference auto R,
PointOriginFor<get_quantity_spec(R)> auto PO = absolute_point_origin<get_quantity_spec(R)>{}, PointOriginFor<get_quantity_spec(R)> auto PO,
RepresentationOf<get_quantity_spec(R).character> Rep = double> RepresentationOf<get_quantity_spec(R).character> Rep = double>
class quantity_point; class quantity_point;
``` ```
As we can see above, the `quantity_point` class template exposes one additional parameter compared As we can see above, the `quantity_point` class template exposes one additional parameter compared
to `quantity`. The `PO` parameter satisfies a [`PointOriginFor` concept](basic_concepts.md#pointoriginfor) to `quantity`. The `PO` parameter satisfies a [`PointOriginFor` concept](basic_concepts.md#PointOriginFor)
and specifies the origin of our scale. and specifies the origin of our measurement scale.
As a _point_ can be represented with a _vector_ from the origin, a `quantity_point` class
### The origin template can be created with the following operations:
The **origin** specifies where the "zero" of our measurement's scale is.
Please notice that a _point_ can be represented with a _vector_ from the origin. This is why in
the **mp-units** library, a `quantity_point` gets a `quantity` in its constructor. Such a `quantity`:
- specifies the relative distance of a specific point from the scale origin,
- is the only data member of the `quantity_point` class template,
- can be obtained with the `relative()` member function.
```cpp ```cpp
constexpr quantity_point<isq::altitude[m]> everest_base_camp_alt{5364 * m}; quantity_point qp1 = mean_sea_level + 42 * m;
static_assert(everest_base_camp_alt.relative() == 5364 * m); quantity_point qp2 = 42 * m + mean_sea_level;
quantity_point qp3 = mean_sea_level - 42 * m;
``` ```
!!! note !!! note
As the constructor is explicit, the quantity point object can only be created from a quantity via [It is not allowed to subtract a _point_ from a _vector_](#operations-in-the-affine-space)
direct initialization. This is why the code below that uses copy initialization does not compile: thus `42 * m - mean_sea_level` is an invalid operation.
```cpp Similarly to [creation of a quantity](../../getting_started/quick_start.md#creating-a-quantity),
quantity_point<isq::altitude[m]> everest_base_camp_alt = 5364 * m; // ERROR if someone does not like the operator-based syntax to create a `quantity_point`, the same results
``` can be achieved with `make_quantity_point` factory function:
In the **mp-units** library, the origin is either provided implicitly (as above) or can be predefined
by the user and then provided explicitly as the `quantity_point` class template argument:
```cpp ```cpp
constexpr struct mean_sea_level : absolute_point_origin<isq::altitude> {} mean_sea_level; quantity_point qp4 = make_quantity_point<mean_sea_level>(42 * m);
quantity_point qp5 = make_quantity_point<mean_sea_level>(-42 * m);
constexpr quantity_point<isq::altitude[m], mean_sea_level> everest_base_camp_alt{5364 * m};
static_assert(everest_base_camp_alt.relative() == 5364 * m);
``` ```
!!! note The provided `quantity` representing an offset from the origin is stored inside the `quantity_point`
class template and can be obtained with a `quantity_from_origin()` member function:
The `mean_sea_level` and the default `absolute_point_origin<isq::altitude>` origins are distinct from ```cpp
each other, which means that _points_ defined with them are not compatible (can't be subtracted or constexpr quantity_point everest_base_camp_alt = mean_sea_level + isq::altitude(5364 * m);
compared). static_assert(everest_base_camp_alt.quantity_from_origin() == 5364 * m);
```
### Class Template Argument Deduction (CTAD) ### Relative _point_ origin
Typing the entire `quantity_point` type may sometimes be quite verbose. Also, please note that we
"accidentally" used `double` as a representation type in the above examples, even though we operated
only on integral values. This was done for the convenience of saving typing.
To improve the developer's experience, the `quantity_point` class template comes with the user-defined
class template argument deduction guides. Thanks to them, the above definitions can be rewritten as
follows:
- implicit default origin
```cpp
constexpr quantity_point everest_base_camp_alt{isq::altitude(5364 * m)};
```
- explicit origin
```cpp
constexpr quantity_point everest_base_camp_alt{isq::altitude(5364 * m), mean_sea_level};
```
### Relative _point_ origins
We often do not have only one ultimate "zero" point when we measure things. We often do not have only one ultimate "zero" point when we measure things.
Continuing the Mount Everest trip example above, measuring all daily hikes from the `mean_sea_level` Continuing the Mount Everest trip example above, measuring all daily hikes from the `mean_sea_level`
might not be efficient. Maybe we know that we are not good climbers, so all our climbs can be might not be efficient. Maybe we know that we are not good climbers, so all our climbs can be
represented with an 8-bit integer type which will allow us to save memory in our database of climbs? represented with an 8-bit integer type allowing us to save memory in our database of climbs?
Why not use `everest_base_camp_alt` as our reference point? Why not use `everest_base_camp_alt` as our reference point?
For this purpose, we can define a `relative_point_origin` in the following way: For this purpose, we can define a `relative_point_origin` in the following way:
@@ -150,31 +132,18 @@ constexpr struct everest_base_camp : relative_point_origin<everest_base_camp_alt
The above can be used as an origin for subsequent _points_: The above can be used as an origin for subsequent _points_:
```cpp ```cpp
constexpr quantity_point<isq::altitude[m], everest_base_camp, std::uint8_t> first_climb_alt{42 * m}; constexpr quantity_point first_climb_alt = everest_base_camp + isq::altitude(std::uint8_t{42} * m);
static_assert(first_climb_alt.relative() == 42 * m); static_assert(first_climb_alt.quantity_from_origin() == 42 * m);
``` ```
As we can see above, the `relative()` member function returns a relative distance from the current As we can see above, the `quantity_from_origin()` member function returns a relative distance from
point origin. In case we would like to know the absolute altitude that we reached on this climb, the current point origin. In case we would like to know the absolute altitude that we reached on this climb,
we can either: we can subtract the absolute point origin from the current _point_:
- add the two relative heights from both _points_ ```cpp
static_assert(first_climb_alt - mean_sea_level == 5406 * m);
```cpp static_assert(first_climb_alt - first_climb_alt.absolute_point_origin == 5406 * m);
static_assert(first_climb_alt.relative() + everest_base_camp_alt.relative() == 5406 * m); ```
```
- subtract the "zero altitude" _point_ from the current _point_
```cpp
static_assert(first_climb_alt - quantity_point{0 * m, mean_sea_level} == 5406 * m);
```
- call `absolute()` member function on the current _point_
```cpp
static_assert(first_climb_alt.absolute() == 5406 * m);
```
### Converting between different representations of the same _point_ ### Converting between different representations of the same _point_
@@ -188,22 +157,23 @@ For this purpose, we can either use:
- a converting constructor: - a converting constructor:
```cpp ```cpp
static_assert(quantity_point<isq::altitude[m], mean_sea_level>{first_climb_alt}.relative() == 5406 * m); constexpr quantity_point<isq::altitude[m], mean_sea_level, int> qp = first_climb_alt;
static_assert(qp.quantity_from_origin() == 5406 * m);
``` ```
- a dedicated conversion interface: - a dedicated conversion interface:
```cpp ```cpp
constexpr QuantityPoint auto qp = first_climb_alt.point_from(mean_sea_level); constexpr quantity_point qp = first_climb_alt.point_for(mean_sea_level);
static_assert(qp.relative() == 5406 * m); static_assert(qp.quantity_from_origin() == 5406 * m);
``` ```
!!! note !!! note
It is allowed to only covert between various origins defined in terms of the same It is only allowed to convert between various origins defined in terms of the same
`absolute_point_origin`. Even if it is possible to express the same _point_ as a _vector_ `absolute_point_origin`. Even if it is possible to express the same _point_ as a _vector_
from another `absolute_point_origin`, the **mp-units** library will not allow it, and from another `absolute_point_origin`, the library will not provide such a conversion.
a custom user-defined conversion function will be needed to provide such a functionality. A custom user-defined conversion function will be needed to add this functionality.
Said otherwise, in the **mp-units** library, there is no way to spell how two distinct Said otherwise, in the **mp-units** library, there is no way to spell how two distinct
`absolute_point_origin` types relate to each other. `absolute_point_origin` types relate to each other.
@@ -213,19 +183,19 @@ For this purpose, we can either use:
Let's assume we will attend the CppCon conference hosted in Aurora, CO, and we want to estimate Let's assume we will attend the CppCon conference hosted in Aurora, CO, and we want to estimate
the distance we will travel. We have to take a taxi to a local airport, fly to DEN airport with the distance we will travel. We have to take a taxi to a local airport, fly to DEN airport with
a stopover in FRA, and in the end, get a cab to the Gaylord Rockies Resort & Convention Center: a stopover in FRA, and, in the end, get a cab to the Gaylord Rockies Resort & Convention Center:
```cpp ```cpp
constexpr struct home_location : absolute_point_origin<isq::distance> {} home_location; constexpr struct home : absolute_point_origin<isq::distance> {} home;
quantity_point<isq::distance[km], home_location> home{}; quantity_point<isq::distance[km], home> home_airport = home + 15 * km;
quantity_point<isq::distance[km], home_location> home_airport = home + 15 * km; quantity_point<isq::distance[km], home> fra_airport = home_airport + 829 * km;
quantity_point<isq::distance[km], home_location> fra_airport = home_airport + 829 * km; quantity_point<isq::distance[km], home> den_airport = fra_airport + 8115 * km;
quantity_point<isq::distance[km], home_location> den_airport = fra_airport + 8115 * km; quantity_point<isq::distance[km], home> cppcon_venue = den_airport + 10.1 * mi;
quantity_point<isq::distance[km], home_location> cppcon_venue = den_airport + 10.1 * mi;
``` ```
As we can see above, we can easily get a new point by adding a quantity to another quantity point. As we can see above, we can easily get a new point by adding a quantity to an origin or another
quantity point.
If we want to find out the distance traveled between two points, we simply subtract them: If we want to find out the distance traveled between two points, we simply subtract them:
@@ -234,8 +204,8 @@ quantity<isq::distance[km]> total = cppcon_venue - home;
quantity<isq::distance[km]> flight = den_airport - home_airport; quantity<isq::distance[km]> flight = den_airport - home_airport;
``` ```
If we would like to find out the total distance traveled by taxi as well, we have to do more If we would like to find out the total distance traveled by taxi as well, we have to do a bit
calculations: more calculations:
```cpp ```cpp
quantity<isq::distance[km]> taxi1 = home_airport - home; quantity<isq::distance[km]> taxi1 = home_airport - home;
@@ -243,7 +213,7 @@ quantity<isq::distance[km]> taxi2 = cppcon_venue - den_airport;
quantity<isq::distance[km]> taxi = taxi1 + taxi2; quantity<isq::distance[km]> taxi = taxi1 + taxi2;
``` ```
Now if we will print the results: Now, if we print the results:
```cpp ```cpp
std::cout << "Total distance: " << total << "\n"; std::cout << "Total distance: " << total << "\n";
@@ -259,53 +229,85 @@ Flight distance: 8944 km
Taxi distance: 31.2544 km Taxi distance: 31.2544 km
``` ```
!!! note
It is not allowed to subtract two point origins defined in terms of `absolute_point_origin`
(i.e. `mean_sea_level - mean_sea_level`) as those do not contain information about the unit
so we are not able to determine a resulting `quantity` type.
### Temperature support ### Temperature support
Another important example of [relative point origins](#relative-point-origins) is support Another important example of [relative point origins](#relative-point-origins) is support
of temperature quantity points in units different than kelvin [`K`]. of temperature quantity points in units different than kelvin [`K`].
For example, the degree Celsius scale can be implemented as follows: The [SI](../../appendix/glossary.md#si) system definition in the **mp-units** library provides
two predefined point origins:
```cpp ```cpp
constexpr struct ice_point : relative_point_origin<quantity_point<isq::thermodynamic_temperature[K]>{273.15 * K}> {} ice_point; namespace mp_units::si {
using Celsius_point = quantity_point<isq::thermodynamic_temperature[deg_C], ice_point>;
inline constexpr struct absolute_zero : absolute_point_origin<isq::thermodynamic_temperature> {} absolute_zero;
inline constexpr struct ice_point : relative_point_origin<absolute_zero + 273.15 * kelvin> {} ice_point;
}
```
With the above, we can be explicit what is the origin of our temperature point. For example, if
we want to implement the degree Celsius scale we can do it as follows:
```cpp
using Celsius_point = quantity_point<isq::Celsius_temperature[deg_C], si::ice_point>;
``` ```
!!! note !!! note
Notice that while stacking point origins, we can use not only different representation types Notice that while stacking point origins, we can use not only different representation types
but also different units for an origin and a _point_. but also different units for an origin and a _point_. In the above example, the relative
point origin is defined in terms of `si::kelvin`, while the quantity point uses
`si::degree_Celsius`.
With the above, for example, if we want to implement a room temperature controller, we can type: To play a bit w temperatures we can implement a simple room's AC temperature controller in
the following way:
```cpp ```cpp
constexpr struct room_reference_temperature : relative_point_origin<Celsius_point{21 * deg_C}> {} room_reference_temperature; constexpr struct room_reference_temp : relative_point_origin<si::ice_point + 21 * deg_C> {} room_reference_temp;
using room_temperature = quantity_point<isq::thermodynamic_temperature[deg_C], room_reference_temperature>; using room_temp = quantity_point<isq::Celsius_temperature[deg_C], room_reference_temp>;
constexpr auto step_delta = isq::thermodynamic_temperature(0.5 * deg_C); constexpr auto step_delta = isq::Celsius_temperature(0.5 * deg_C);
constexpr int number_of_steps = 6; constexpr int number_of_steps = 6;
room_temperature room_default{}; room_temp room_low = room_reference_temp - number_of_steps * step_delta;
room_temperature room_low = room_default - number_of_steps * step_delta; room_temp room_high = room_reference_temp + number_of_steps * step_delta;
room_temperature room_high = room_default + number_of_steps * step_delta;
std::cout << "Lowest temp: " << room_low.relative() << " (" << room_low - Celsius_point::zero() << ")\n"; std::println("| {:<14} | {:^18} | {:^18} | {:^18} |", "Temperature", "Room reference", "Ice point", "Absolute zero");
std::cout << "Highest temp: " << room_high.relative() << " (" << room_high - Celsius_point::zero() << ")\n"; std::println("|{0:=^16}|{0:=^20}|{0:=^20}|{0:=^20}|", "");
auto print = [&](std::string_view label, auto v){
std::println("| {:<14} | {:^18} | {:^18} | {:^18} |",
label, v - room_reference_temp, v - si::ice_point, v - si::absolute_zero);
};
print("Lowest", room_low);
print("Default", room_reference_temp);
print("Highest", room_high);
``` ```
The above prints: The above prints:
```text ```text
Lowest temp: -3 °C (18 °C) | Temperature | Room reference | Ice point | Absolute zero |
Highest temp: 3 °C (24 °C) |================|====================|====================|====================|
| Lowest | -3 °C | 18 °C | 291.15 °C |
| Default | 0 °C | 21 °C | 294.15 °C |
| Highest | 3 °C | 24 °C | 297.15 °C |
``` ```
### No text output for _points_ ### No text output for _points_
The library does not provide a text output for quantity points, as printing just a number and a unit The library does not provide a text output for quantity points, as printing just a number and a unit
is not enough to adequately describe a quantity point. Often an additional postfix is required. is not enough to adequately describe a quantity point. Often, an additional postfix is required.
For example, the text output of `42 m` may mean many things and can also be confused with an output For example, the text output of `42 m` may mean many things and can also be confused with an output
of a regular quantity. On the other hand, printing `42 m AMSL` for altitudes above mean sea level is of a regular quantity. On the other hand, printing `42 m AMSL` for altitudes above mean sea level is
@@ -316,23 +318,26 @@ a much better solution, but the library does not have enough information to prin
The following operations are not allowed in the affine space: The following operations are not allowed in the affine space:
- **add** two `quantity_point` objects (It is physically impossible to add positions of home - **adding** two `quantity_point` objects
and Denver airports), - It is physically impossible to add positions of home and Denver airports.
- **subtract** a `quantity_point` from a `quantity` (What would it mean to subtract DEN airport - **subtracting** a `quantity_point` from a `quantity`
location from the distance to it?), - What would it mean to subtract DEN airport location from the distance to it?
- **multiply/divide** a `quantity_point` with a scalar (What is the position of `2x` DEN airport location?). - **multiplying/dividing** a `quantity_point` with a scalar
- **multiply/divide** a `quantity_point` with a quantity (What would multiplying the distance with the - What is the position of `2 *` DEN airport location?
DEN airport location mean?). - **multiplying/dividing** a `quantity_point` with a quantity
- **multiply/divide** two `quantity_point` objects (What would multiplying home and DEN airport location mean?). - What would multiplying the distance with the DEN airport location mean?
- **mix** `quantity_points` of different quantity kinds (It is physically impossible to subtract time - **multiplying/dividing** two `quantity_point` objects
from length), - What would multiplying home and DEN airport location mean?
- **mix** `quantity_points` of inconvertible quantities (What does it mean to subtract a distance - **mixing** `quantity_points` of different quantity kinds
point to DEN airport from the Mount Everest base camp altitude?), - It is physically impossible to subtract time from length.
- **mix** `quantity_points` of convertible quantities but with unrelated origins (How to subtract - **mixing** `quantity_points` of inconvertible quantities
a point on our trip to CppCon measured relatively to our home location from a point measured - What does it mean to subtract a distance point to DEN airport from the Mount Everest base camp
relative to the center of the Solar System?). altitude?
- **mixing** `quantity_points` of convertible quantities but with unrelated origins
- How to subtract a point on our trip to CppCon measured relatively to our home location from
a point measured relative to the center of the Solar System?
!!! note !!! note
The usage of `quantity_point`, and affine space types in general, improves expressiveness and The usage of `quantity_point` and affine space types in general, improves expressiveness and
type-safety of the code we write. type-safety of the code we write.