diff --git a/docs/users_guide/framework_basics/affine_space_1.svg b/docs/users_guide/framework_basics/affine_space_1.svg
new file mode 100644
index 00000000..d0475656
--- /dev/null
+++ b/docs/users_guide/framework_basics/affine_space_1.svg
@@ -0,0 +1,216 @@
+
+
+
+
diff --git a/docs/users_guide/framework_basics/affine_space_2.svg b/docs/users_guide/framework_basics/affine_space_2.svg
new file mode 100644
index 00000000..c6ad90e5
--- /dev/null
+++ b/docs/users_guide/framework_basics/affine_space_2.svg
@@ -0,0 +1,216 @@
+
+
+
+
diff --git a/docs/users_guide/framework_basics/affine_space_3.svg b/docs/users_guide/framework_basics/affine_space_3.svg
new file mode 100644
index 00000000..66323fca
--- /dev/null
+++ b/docs/users_guide/framework_basics/affine_space_3.svg
@@ -0,0 +1,291 @@
+
+
+
+
diff --git a/docs/users_guide/framework_basics/affine_space_4.svg b/docs/users_guide/framework_basics/affine_space_4.svg
new file mode 100644
index 00000000..81024c8c
--- /dev/null
+++ b/docs/users_guide/framework_basics/affine_space_4.svg
@@ -0,0 +1,449 @@
+
+
+
+
diff --git a/docs/users_guide/framework_basics/affine_space_5.svg b/docs/users_guide/framework_basics/affine_space_5.svg
new file mode 100644
index 00000000..8ee91d6e
--- /dev/null
+++ b/docs/users_guide/framework_basics/affine_space_5.svg
@@ -0,0 +1,243 @@
+
+
+
+
diff --git a/docs/users_guide/framework_basics/affine_space_6.svg b/docs/users_guide/framework_basics/affine_space_6.svg
new file mode 100644
index 00000000..4998f32f
--- /dev/null
+++ b/docs/users_guide/framework_basics/affine_space_6.svg
@@ -0,0 +1,462 @@
+
+
+
+
diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md
index 56b60dd7..9b59dd3f 100644
--- a/docs/users_guide/framework_basics/the_affine_space.md
+++ b/docs/users_guide/framework_basics/the_affine_space.md
@@ -3,13 +3,16 @@
The affine space has two types of entities:
- **_Point_** - a position specified with coordinate values (e.g., location, address, etc.)
-- **_Vector_** - the difference between two points (e.g., shift, offset, displacement, duration, etc.)
+- **_Displacement vectors_** - the difference between two points (e.g., shift, offset,
+ displacement, duration, etc.)
+In the following subchapters, we will often refer to _displacement vectors_ simply as _vectors_ for
+brevity.
!!! note
- The _Vector_ described here is specific to the affine space theory and is not the same thing
- as the quantity of a vector character that we discussed in the
+ The _displacement vector_ described here is specific to the affine space theory and is not the same
+ thing as the quantity of a vector character that we discussed in the
["Scalars, vectors, and tensors" chapter](character_of_a_quantity.md#scalars-vectors-and-tensors)
(although, in some cases, those terms may overlap).
@@ -18,24 +21,24 @@ The affine space has two types of entities:
Here are the primary operations one can do in the affine space:
-- _Vector_ + _Vector_ -> _Vector_
-- _Vector_ - _Vector_ -> _Vector_
-- -_Vector_ -> _Vector_
-- _Vector_ * Scalar -> _Vector_
-- Scalar * _Vector_ -> _Vector_
-- _Vector_ / Scalar -> _Vector_
-- _Point_ - _Point_ -> _Vector_
-- _Point_ + _Vector_ -> _Point_
-- _Vector_ + _Point_ -> _Point_
-- _Point_ - _Vector_ -> _Point_
+- _vector_ + _vector_ -> _vector_
+- _vector_ - _vector_ -> _vector_
+- -_vector_ -> _vector_
+- _vector_ * scalar -> _vector_
+- scalar * _vector_ -> _vector_
+- _vector_ / scalar -> _vector_
+- _point_ - _point_ -> _vector_
+- _point_ + _vector_ -> _point_
+- _vector_ + _point_ -> _point_
+- _point_ - _vector_ -> _point_
!!! important
It is not possible to:
- - add two _Points_,
- - subtract a _Point_ from a _Vector_,
- - multiply nor divide _Points_ with anything else.
+ - add two _points_,
+ - subtract a _point_ from a _vector_,
+ - multiply nor divide _points_ with anything else.
## _Points_ are more common than most of us imagine
@@ -55,7 +58,7 @@ more popular in the products we implement. They can be used to implement:
Improving the affine space's _Points_ intuition will allow us to write better and safer software.
-## _Vector_ is modeled by `quantity`
+## _Displacement vector_ is modeled by `quantity`
Up until now, each time we used a `quantity` in our code, we were modeling some kind of a
difference between two things:
@@ -64,15 +67,15 @@ difference between two things:
- _duration_ between two time points,
- the difference in _speed_ (even if relative to zero).
-As we already know, a `quantity` type provides all operations required for a _Vector_ type in
-the affine space.
+As we already know, a `quantity` type provides all operations required for a _displacement vcector_
+abstraction in an affine space.
## _Point_ is modeled by `quantity_point` and `PointOrigin`
-In the **mp-units** library the _Point_ abstraction is modelled by:
+In the **mp-units** library, the _Point_ abstraction is modelled by:
-- [`PointOrigin` concept](concepts.md#PointOrigin) that specifies measurement origin,
+- [`PointOrigin` concept](concepts.md#PointOrigin) that specifies measurement origin, and
- `quantity_point` class template that specifies a _Point_ relative to a specific predefined origin.
@@ -90,125 +93,119 @@ class quantity_point;
As we can see above, the `quantity_point` class template exposes one additional parameter compared
to `quantity`. The `PO` parameter satisfies a [`PointOriginFor` concept](concepts.md#PointOriginFor)
-and specifies the origin of our measurement scale. By default, it is initialized with a quantity's
-zeroth point using the following rules:
+and specifies the origin of our measurement scale.
+
+Each `quantity_point` internally stores a `quantity` object, which represents a _displacement vector_
+from the predefined origin. Thanks to this, an instantiation of a `quantity_point` can be considered
+as a model of a vector space from such an origin.
+
+Forcing the user to manually predefine an origin for every domain may be cumbersome and discourage
+users from using such abstractions at all. This is why, by default, the `PO` template
+parameter is initialized with the `default_point_origin(R)` that provides the quantity points'
+scale zeroth point using the following rules:
- if the measurement unit of a quantity specifies its point origin in its definition
(e.g., degree Celsius), then this point is being used,
- otherwise, an instantiation of `zeroth_point_origin` is being used which
- provides a zeroth point for a specific quantity type.
+ provides a well-established zeroth point for a specific quantity type.
!!! tip
The `quantity_point` definition can be found in the `mp-units/quantity_point.h` header file.
-### Implicit point origin
+#### `zeroth_point_origin`
-Let's assume that Alice goes for a trip driving a car. She likes taking notes about interesting
-places that she visits on the road. For every such item, she writes down:
+`zeroth_point_origin` is meant to be used in cases where the specific domain has
+a well-established, non-controversial zeroth point on the measurement scale. This saves the user
+from the need to write a boilerplate code that would predefine such a type for such a domain.
-- its name,
-- a readout from the car's odometer at the location,
-- a current timestamp.
-
-We can implement this in the following way:
+{style="width:80%;display: block;margin: 0 auto;"}
```cpp
-using std::chrono::system_clock;
+quantity_point qp1{100 * m};
+quantity_point qp2{120 * m};
-struct trip_log_item {
- std::string name;
- quantity_point odometer;
- quantity_point timestamp;
-};
-using trip_log = std::vector;
+assert(qp1.quantity_from_zero() == 100 * m);
+assert(qp2.quantity_from_zero() == 120 * m);
+
+assert(qp2 - qp1 == 20 * m);
+assert(qp1 - qp2 == -20 * m);
+
+// auto res = qp1 + qp2; // Compile-time error
```
+In the above code `100 * m` and `120 * m` still create two quantities that serve as _displacement
+vectors_ here. Quantity point objects can be explicitly constructed from such quantities only when
+their origin is an instantiation of the `zeroth_point_origin`.
+
+It is really important to understand that even though we can use `.quantity_from_zero()` to obtain
+the _displacement vector_ of a point from the origin, the point by itself does not represent or have
+any associated physical value. It is just a point in some space. The same point can be expressed
+with different _displacement vectors_ from different origins.
+
+It is also worth mentioning that simplicity comes with a safety cost here. For some users, it
+might be surprising that the usage of `zeroth_point_origin` makes various quantity
+point objects compatible as long as quantity types used in the origin and reference are
+compatible:
+
```cpp
-trip_log log;
+quantity_point qp1{isq::distance(100 * m)};
+quantity_point qp2{isq::height(120 * m)};
-quantity_point timestamp_1{quantity{system_clock::now().time_since_epoch()}};
-log.emplace_back("home", quantity_point{1356 * km}, timestamp_1);
-
-// some time passes
-
-quantity_point timestamp_2{quantity{system_clock::now().time_since_epoch()}};
-log.emplace_back("castle", quantity_point{1401 * km}, timestamp_2);
+assert(qp2 - qp1 == 20 * m);
+assert(qp1 - qp2 == -20 * m);
```
-This is an excellent example of where points are helpful. There is no doubt about the correctness
-of their usage in this scenario:
-- adding two odometer readouts or two timestamps have no physical sense, and that is why we will
- expect a compile-time error when we try to perform such operations accidentally,
-- subtracting two odometer readouts or timestamps is perfectly valid and results in a quantity
- storing the interval value between the two points.
+### Absolute _point_ origin
-Having such a database, we can print the trip log in the following way:
+In cases where we want to implement an isolated independent space in which points are not compatible
+with other spaces, even of the same quantity type, we should manually predefine an absolute point
+origin.
+
+{style="width:80%;display: block;margin: 0 auto;"}
```cpp
-for (const auto& item : log) {
- std::cout << "POI: " << item.name << "\n";
- std::cout << "- Distance from home: " << item.odometer - log.front().odometer << "\n";
- std::cout << "- Trip duration from start: " << (item.timestamp - log.front().timestamp).in(non_si::minute) << "\n";
-}
+inline constexpr struct origin : absolute_point_origin {} origin;
+
+// quantity_point qp1{100 * m}; // Compile-time error
+// quantity_point qp2{120 * m}; // Compile-time error
+quantity_point qp1 = origin + 100 * m;
+quantity_point qp2 = 120 * m + origin;
+
+// assert(qp1.quantity_from_zero() == 100 * m); // Compile-time error
+// assert(qp2.quantity_from_zero() == 120 * m); // Compile-time error
+assert(qp1.quantity_from(origin) == 100 * m);
+assert(qp2.quantity_from(origin) == 120 * m);
+
+assert(qp1 - origin == 100 * m);
+assert(qp2 - origin == 120 * m);
+assert(origin - qp1 == -100 * m);
+assert(origin - qp2 == -120 * m);
+
+assert(qp2 - qp1 == 20 * m);
```
-Moreover, if Alice had reset the car's trip odometer before leaving home, we could have rewritten
-one of the previous lines like that:
+This time, we can't construct a quantity point from any quantity. In order to prevent potential safety
+issues, when a custom point origin is being used, we always need to provide its object in
+an expression that results in a quantity point instantiation.
+
+!!! info
+
+ A rationale for this longer construction syntax can be found in the
+ [Why can't I create a quantity by passing a number to a constructor?](../../getting_started/faq.md#why-cant-i-create-a-quantity-by-passing-a-number-to-a-constructor)
+ chapter.
+
+Similarly to [creation of a quantity](../../getting_started/quick_start.md#creating-a-quantity),
+if someone does not like the operator-based syntax to create a `quantity_point`, the same results
+can be achieved with a two-parameter constructor:
```cpp
-std::cout << "Distance from home: " << item.odometer.quantity_from_zero() << "\n";
+quantity_point qp1{100 * m, origin};
```
-The above always returns a quantity measured from the "ultimate" zeroth point of a scale used for
-this specific quantity type.
-
-!!! tip
-
- Storing _Points_ is the most efficient representation we can choose in this scenario:
-
- - to store a value, we read it directly from the instrument, and no additional transformation
- is needed,
- - to print the absolute value (e.g., odometer), we have the value available right away,
- - to get any relative quantity (e.g., distance from the start, distance from the previous point,
- etc.), we have to perform a single subtraction operation.
-
- If we stored _Vectors_ in our database instead, we would have to pay at runtime for additional
- operations:
-
- - to store a quantity, we would have to perform the subtraction right away to get the interval
- between the current value and some reference point,
- - to print the absolute value, we would have to add the quantity to the reference point that
- we need to store somewhere in the database as well,
- - to get a relative quantity, only the currently stored one is immediate; all other values
- will require at least one quantity addition operation.
-
-Now, let's assume that Bob, a friend of Alice, also keeps a log of his trips but he, of
-course, measures distances from his own home with the odometer in his car. Everything is fine as
-long as we deal with one trip at a time, but if we start to work with both at once, we may
-accidentally subtract points from different trips. The library will not prevent
-us from doing so.
-
-The points from Alice's and Bob's trips should be considered separate, and to enforce it at
-compilation time, we need to introduce explicit origins.
-
-
-### Absolute _Point_ origin
-
-The **absolute point origin** specifies the "zero" of our measurement's scale. User can
-specify such an origin by deriving from the `absolute_point_origin` class template:
-
-```cpp
-enum class actor { alice, bob };
-
-template
-struct zeroth_odometer_t : absolute_point_origin, isq::distance> {};
-
-template
-inline constexpr zeroth_odometer_t zeroth_odometer;
-```
+Again, CTAD always helps to use precisely the type we need in a current case.
!!! info
@@ -225,212 +222,148 @@ inline constexpr zeroth_odometer_t zeroth_odometer;
- we can't define the above in one line of code,
- provide the same identifier for a class and variable template.
-Odometer is not the only one that can get an explicit point origin in our case. As timestamps are
-provided by the `std::chrono::system_clock`, their values are always relative to the epoch of this
-clock.
+Finally, please note that it is not allowed to subtract two point origins defined in terms of
+`absolute_point_origin` (e.g., `origin - origin`) as those do not contain information about the
+unit, so we cannot determine a resulting `quantity` type.
-!!! note
+Absolute point origins are also perfect for establishing independent spaces even if the same quantity
+type and unit is being used:
- The **mp-units** library provides means to specify
- [interoperability with other units libraries](../use_cases/interoperability_with_other_libraries.md).
- It also has built-in compatibility with `std::chrono` types, so users do not have to define
- interoperability traits or point origins for such types by themselves. Those are already
- provided in the `mp-units/systems/si/chrono.h` header file.
-
-
-Now, we can refactor our database to benefit from the explicit points:
+{style="width:80%;display: block;margin: 0 auto;"}
```cpp
-template
-struct trip_log_item {
- std::string point_name;
- quantity_point, zeroth_odometer> odometer;
- quantity_point> timestamp;
-};
+inline constexpr struct origin1 : absolute_point_origin {} origin1;
+inline constexpr struct origin2 : absolute_point_origin {} origin2;
-template
-using trip_log = std::vector>;
+quantity_point qp1 = origin1 + 100 * m;
+quantity_point qp2 = origin2 + 120 * m;
+
+assert(qp1.quantity_from(origin1) == 100 * m);
+assert(qp2.quantity_from(origin2) == 120 * m);
+
+assert(qp1 - origin1 == 100 * m);
+assert(qp2 - origin2 == 120 * m);
+assert(origin1 - qp1 == -100 * m);
+assert(origin2 - qp2 == -120 * m);
+
+// assert(qp2 - qp1 == 20 * m); // Compile-time error
+// assert(qp1 - origin2 == 100 * m); // Compile-time error
+// assert(qp2 - origin1 == 120 * m); // Compile-time error
+// assert(qp1.quantity_from(origin2) == 100 * m); // Compile-time error
+// assert(qp2.quantity_from(origin1) == 120 * m); // Compile-time error
```
-We also need to update the initialization part in our code. In the case of implicit zeroth origins,
-we could construct `quantity_point` directly from the value of a `quantity`. This is no longer
-the case.
-As a _Point_ can be represented with a _Vector_ from the origin, to improve the safety of the code
-we write, a `quantity_point` class template must be created with one of the following operations:
-
-```cpp
-quantity_point qp1 = zeroth_odometer + 1356 * km;
-quantity_point qp2 = 1356 * km + zeroth_odometer;
-quantity_point qp3 = zeroth_odometer - 1356 * km;
-```
-
-Although, the `qp3` above does not have a physical sense in this specific scenario.
-
-!!! note
-
- [It is not allowed to subtract a _Point_ from a _Vector_](#operations-in-the-affine-space)
- thus `1356 * km - zeroth_odometer` is an invalid operation.
-
-!!! info
-
- A rationale for this longer construction syntax can be found in the
- [Why can't I create a quantity by passing a number to a constructor?](../../getting_started/faq.md#why-cant-i-create-a-quantity-by-passing-a-number-to-a-constructor)
- chapter.
-
-Similarly to [creation of a quantity](../../getting_started/quick_start.md#creating-a-quantity),
-if someone does not like the operator-based syntax to create a `quantity_point`, the same results
-can be achieved with a two-parameter constructor:
-
-```cpp
-quantity_point qp4{1356 * km, zeroth_odometer};
-```
-
-Also, as now our timestamps have a proper point origin provided in a type, we can simplify the
-previous code by directly converting `std::chrono::time_point` to our `quantity_point` type.
-
-With all the above, we can refactor our initialization part to the following:
-
-```cpp
-trip_log alice_log;
-
-alice_log.emplace_back("home", zeroth_odometer + 1356 * km, system_clock::now());
-
-// some time passes
-
-alice_log.emplace_back("castle", zeroth_odometer + 1401 * km, system_clock::now());
-```
-
-
-### _Point_ arithmetics
-
-As another example, 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 a stopover in FRA, and, in the end, get a cab to the Gaylord Rockies
-Resort & Convention Center:
-
-```cpp
-constexpr struct home : absolute_point_origin {} home;
-
-quantity_point home_airport = home + 15 * km;
-quantity_point fra_airport = home_airport + 829 * km;
-quantity_point den_airport = fra_airport + 8115 * km;
-quantity_point cppcon_venue = den_airport + 10.1 * mi;
-```
-
-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:
-
-```cpp
-quantity total = cppcon_venue - home;
-quantity flight = den_airport - home_airport;
-```
-
-If we would like to find out the total distance traveled by taxi as well, we have to do a bit
-more calculations:
-
-```cpp
-quantity taxi1 = home_airport - home;
-quantity taxi2 = cppcon_venue - den_airport;
-quantity taxi = taxi1 + taxi2;
-```
-
-Now, if we print the results:
-
-```cpp
-std::cout << "Total distance: " << total << "\n";
-std::cout << "Flight distance: " << flight << "\n";
-std::cout << "Taxi distance: " << taxi << "\n";
-```
-
-we will see the following output:
-
-```text
-Total distance: 8975.25 km
-Flight distance: 8944 km
-Taxi distance: 31.2544 km
-```
-
-!!! note
-
- It is not allowed to subtract two point origins defined in terms of `absolute_point_origin`
- (e.g., `home - home`) as those do not contain information about the unit, so we are not able
- to determine a resulting `quantity` type.
-
### Relative _Point_ origin
-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. Often, we have one
+common scale, but we measure various quantities relative to different points and expect
+those points to be compatible. There are many examples here, but probably the most common are
+temperatures, timestamps, and altitudes.
-For example, let's assume that we have the following absolute point origin:
+For such cases, relative point origins should be used:
+
+{style="width:80%;display: block;margin: 0 auto;"}
```cpp
-constexpr struct mean_sea_level : absolute_point_origin {} mean_sea_level;
-```
+inline constexpr struct A : absolute_point_origin {} A;
+inline constexpr struct B : relative_point_origin {} B;
+inline constexpr struct C : relative_point_origin {} C;
+inline constexpr struct D : relative_point_origin {} D;
-If we want to model a trip to Mount Everest, measuring all daily hikes from the `mean_sea_level`
-might not be efficient. We may know that we are not good climbers, so all our climbs can be
-represented with an 8-bit integer type, allowing us to save memory in our database of climbs.
+quantity_point qp1 = C + 100 * m;
+quantity_point qp2 = D + 120 * m;
-For this purpose, we can define a `relative_point_origin` in the following way:
+assert(qp1.quantity_ref_from(qp1.point_origin) == 100 * m);
+assert(qp2.quantity_ref_from(qp2.point_origin) == 120 * m);
-```cpp
-constexpr struct everest_base_camp : relative_point_origin {} everest_base_camp;
-```
+assert(qp1 - A == 120 * m);
+assert(qp1 - B == 110 * m);
+assert(qp1 - C == 100 * m);
+assert(qp1 - D == 90 * m);
+assert(qp1.quantity_from(A) == 120 * m);
+assert(qp1.quantity_from(B) == 110 * m);
+assert(qp1.quantity_from(C) == 100 * m);
+assert(qp1.quantity_from(D) == 90 * m);
-The above can be used as an origin for subsequent _Points_:
+assert(qp2 - A == 150 * m);
+assert(qp2 - B == 140 * m);
+assert(qp2 - C == 130 * m);
+assert(qp2 - D == 120 * m);
+assert(qp2.quantity_from(A) == 150 * m);
+assert(qp2.quantity_from(B) == 140 * m);
+assert(qp2.quantity_from(C) == 130 * m);
+assert(qp2.quantity_from(D) == 120 * m);
-```cpp
-constexpr quantity_point first_climb_alt = everest_base_camp + isq::altitude(std::uint8_t{42} * m);
-static_assert(first_climb_alt.quantity_from(everest_base_camp) == 42 * m);
-static_assert(first_climb_alt.quantity_from(mean_sea_level) == 5406 * m);
-static_assert(first_climb_alt.quantity_from_zero() == 5406 * m);
+assert(qp2 - qp1 == 30 * m);
+
+assert(B - A == 10 * m);
+assert(C - A == 20 * m);
+assert(D - A == 30 * m);
+assert(D - C == 10 * m);
```
As we can see above, the `quantity_from()` member function returns a relative distance from the
-provided point origin while the `quantity_from_zero()` returns the distance from the absolute point
-origin.
+provided point origin while the `quantity_from_zero()` always returns the distance from the
+absolute point origin.
+
+Also, please note that as long as we can't subtract two absolute point origins from each other,
+it is possible to subtract relative ones or a relative and absolute one.
-### Converting between different representations of the same _Point_
+### Converting between different representations of the same _point_
-As we might represent the same _Point_ with _Vectors_ from various origins, the **mp-units** library
-provides facilities to convert the _Point_ to `quantity_point` class templates expressed in
-terms of origins relative to each other in the type system.
+As we might represent the same _point_ with _displacement vectors_ from various origins, the
+library provides facilities to convert the same _point_ to the `quantity_point` class templates
+expressed in terms of different origins.
-For this purpose, we can use:
+{style="width:80%;display: block;margin: 0 auto;"}
-- a converting constructor:
+For this purpose, we can use either:
+
+- A converting constructor:
```cpp
- constexpr quantity_point qp = first_climb_alt;
- static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m);
+ quantity_point qp2C = qp2;
+ assert(qp2C.quantity_ref_from(qp2C.point_origin) == 130 * m);
```
-- a dedicated conversion interface:
+- A dedicated conversion interface:
```cpp
- constexpr quantity_point qp = first_climb_alt.point_for(mean_sea_level);
- static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m);
+ quantity_point qp2B = qp2.point_for(B);
+ quantity_point qp2A = qp2.point_for(A);
+
+ assert(qp2B.quantity_ref_from(qp2B.point_origin) == 140 * m);
+ assert(qp2A.quantity_ref_from(qp2A.point_origin) == 150 * m);
```
+It is important to understand that the point remains the same after such a translation
+(all of them compare equal):
+
+```cpp
+assert(qp2 == qp2C);
+assert(qp2 == qp2B);
+assert(qp2 == qp2A);
+```
+
!!! note
It is only allowed to convert between various origins defined in terms of the same
- `absolute_point_origin`. Even if it is theoretically possible to express the same _Point_ as
- a _Vector_ from another `absolute_point_origin`, the library will not allow such a conversion.
- A custom user-defined conversion function will be needed to add this functionality.
+ `absolute_point_origin`. Even if it is possible to express the same _point_ as a
+ _displacement vector_ from another `absolute_point_origin`, the library will not provide such
+ a conversion. 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
- `absolute_point_origin` types relate to each other.
+ Said another way, in the library, there is no way to spell how two distinct `absolute_point_origin`
+ types relate to each other.
### Temperature support
-Another important example of [relative point origins](#relative-point-origins) is the support
-of temperature quantity points. The **mp-units** library provides a few predefined point origins
-for this purpose:
+Support for temperature quantity points is probably one of the most common examples of relative
+point origins in action that we use in daily life.
+
+The [@SI] definition in the library provides a few predefined point origins for this purpose:
```cpp
namespace si {
@@ -459,7 +392,7 @@ The above is a great example of how point origins can be stacked on top of each
!!! note
Notice that while stacking point origins, we can use not only different representation types
- but also different units for origins and a _Point_. In the above example, the relative
+ but also different units for origins and a _point_. In the above example, the relative
point origin for degree Celsius is defined in terms of `si::kelvin`, while the quantity point
for it will use `si::degree_Celsius` as a unit.
@@ -479,13 +412,14 @@ inline constexpr struct degree_Celsius :
namespace usc {
inline constexpr struct degree_Fahrenheit :
- named_unit<{u8"°F", "`F"}, mag * si::degree_Celsius, zeroth_degree_Fahrenheit> {} degree_Fahrenheit;
+ named_unit<{u8"°F", "`F"}, mag * si::degree_Celsius,
+ zeroth_degree_Fahrenheit> {} degree_Fahrenheit;
}
```
-Now let's see how we can benefit from the above definitions. We have quite a few alternatives to
-choose from here. Depending on our needs or taste we can:
+Now, let's see how we can benefit from the above definitions. We have quite a few alternatives to
+choose from here. Depending on our needs or tastes, we can:
- be explicit about the unit and origin:
@@ -511,13 +445,13 @@ choose from here. Depending on our needs or taste we can:
quantity_point q9{20.5 * deg_C};
```
-*[CTAD]: Class Template Argument Deduction
-
In all of the above cases, we end up with the `quantity_point` of the same type and value.
To play a bit more with temperatures, we can implement a simple room AC temperature controller in
the following way:
+{style="width:80%;display: block;margin: 0 auto;"}
+
```cpp
constexpr struct room_reference_temp : relative_point_origin {} room_reference_temp;
using room_temp = quantity_point;
@@ -539,7 +473,7 @@ std::println("| {:<14} | {:^18} | {:^18} | {:^18} |",
std::println("|{0:=^16}|{0:=^20}|{0:=^20}|{0:=^20}|", "");
auto print = [&](std::string_view label, auto v) {
- std::println("| {:<14} | {:^18} | {:^18} | {:^18{%N:.2f} %U} |", label,
+ std::println("| {:<14} | {:^18} | {:^18} | {:^18:N[.2f]} |", label,
v - room_reference_temp, (v - si::ice_point).in(deg_C), (v - si::absolute_zero).in(deg_C));
};
@@ -563,13 +497,20 @@ Room reference temperature: 21 °C (69.8 °F, 294.15 K)
### No text output for _Points_
-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 prefix or postfix is
-required.
+The library does not provide a text output for quantity points. The quantity stored inside
+is just an implementation detail of this type. It is a vector from a specific origin.
+Without the knowledge of the origin, the vector by itself is useless as we can't determine
+which point it describes.
-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
-a much better solution, but the library does not have enough information to print it that way by itself.
+In the current library design, point origin does not provide any text in its definition.
+Even if we could add such information to the point's definition, we would not
+know how to output it in the text. There may be many ways to do it. For example, should we
+prepend or append the origin part to the quantity text?
+
+For example, the text output of `42 m` for a quantity point may mean many things. It may be
+an offset from the mountain top, sea level, or maybe the center of Mars.
+Printing `42 m AMSL` for altitudes above mean sea level is a much better solution, but the
+library does not have enough information to print it that way by itself.
## The affine space is about type-safety