MeshLib
 
Loading...
Searching...
No Matches
MRUnits.h
Go to the documentation of this file.
1#pragma once
2
3#include "MRViewer/exports.h"
5
6#include <cassert>
7#include <optional>
8#include <string>
9#include <variant>
10
11// Read the manual at: docs/measurement_units.md
12
13namespace MR
14{
15
16// A stub measurement unit representing no unit.
17enum class NoUnit
18{
19 _count [[maybe_unused]]
20};
21
22// Measurement units of length.
23enum class LengthUnit
24{
25 mm,
26 inches,
27 _count [[maybe_unused]],
28};
29
30// Measurement units of angle.
31enum class AngleUnit
32{
33 radians,
34 degrees,
35 _count [[maybe_unused]],
36};
37
38// Measurement units of screen sizes.
39enum class PixelSizeUnit
40{
41 pixels,
42 _count [[maybe_unused]],
43};
44
45// Measurement units for factors / ratios.
46enum class RatioUnit
47{
48 factor, // 0..1 x
49 percents, // 0..100 %
50 _count [[maybe_unused]],
51};
52
53// Measurement units for time.
54enum class TimeUnit
55{
56 seconds,
58 _count [[maybe_unused]],
59};
60
61// Measurement units for movement speed.
63{
66 _count [[maybe_unused]],
67};
68
69// Measurement units for surface area.
70enum class AreaUnit
71{
72 mm2,
73 inches2,
74 _count [[maybe_unused]],
75};
76
77// Measurement units for body volume.
78enum class VolumeUnit
79{
80 mm3,
81 inches3,
82 _count [[maybe_unused]],
83};
84
85// Measurement units for 1/length.
86enum class InvLengthUnit
87{
88 inv_mm, // mm^-1
89 inv_inches, // inches^-1
90 _count [[maybe_unused]],
91};
92
93// A list of all unit enums, for internal use.
94#define DETAIL_MR_UNIT_ENUMS(X) X(NoUnit) X(LengthUnit) X(AngleUnit) X(PixelSizeUnit) X(RatioUnit) X(TimeUnit) X(MovementSpeedUnit) X(AreaUnit) X(VolumeUnit) X(InvLengthUnit)
95
96// All supported value types for `valueToString()`.
97#define DETAIL_MR_UNIT_VALUE_TYPES(X, ...) \
98 X(float __VA_OPT__(,)__VA_ARGS__) X(double __VA_OPT__(,)__VA_ARGS__) X(long double __VA_OPT__(,)__VA_ARGS__) \
99 X(signed char __VA_OPT__(,)__VA_ARGS__) X(unsigned char __VA_OPT__(,)__VA_ARGS__) \
100 X(short __VA_OPT__(,)__VA_ARGS__) X(unsigned short __VA_OPT__(,)__VA_ARGS__) \
101 X(int __VA_OPT__(,)__VA_ARGS__) X(unsigned int __VA_OPT__(,)__VA_ARGS__) \
102 X(long __VA_OPT__(,)__VA_ARGS__) X(unsigned long __VA_OPT__(,)__VA_ARGS__) \
103 X(long long __VA_OPT__(,)__VA_ARGS__) X(unsigned long long __VA_OPT__(,)__VA_ARGS__)
104
105// Whether `E` is one of the unit enums: NoUnit, LengthUnit, AngleUnit, ...
106template <typename T>
107concept UnitEnum =
108 #define MR_X(E) || std::same_as<T, E>
110 #undef MR_X
111
112// ---
113
114// Information about a single measurement unit.
116{
117 // This is used to convert between units.
118 // To convert from A to B, multiply by A's factor and divide by B's.
120
121 std::string_view prettyName;
122
123 // The short unit name that's placed after values.
124 // This may or may not start with a space.
125 std::string_view unitSuffix;
126};
127
128// Returns information about a single measurement unit.
129template <UnitEnum E>
130[[nodiscard]] const UnitInfo& getUnitInfo( E unit ) = delete;
131
132#define MR_X(E) template <> [[nodiscard]] MRVIEWER_API const UnitInfo& getUnitInfo( E unit );
134#undef MR_X
135
136// Returns true if converting a value between units `a` and `b` doesn't change its value.
137template <UnitEnum E>
138[[nodiscard]] bool unitsAreEquivalent( E a, E b )
139{
140 return a == b || getUnitInfo( a ).conversionFactor == getUnitInfo( b ).conversionFactor;
141}
142
143namespace detail::Units
144{
145 struct Empty {};
146
147 template <typename T>
148 concept Scalar = std::is_arithmetic_v<T> && !std::is_same_v<T, bool>;
149
150 template <typename T>
151 using MakeFloatingPoint = std::conditional_t<std::is_integral_v<typename VectorTraits<T>::BaseType>, typename VectorTraits<T>::template ChangeBaseType<float>, T>;
152}
153
154// Converts `value` from unit `from` to unit `to`. `value` is a scalar of a Vector2/3/4 or ImVec2/4 of them.
155// The return type matches `T` if it's not integral. If it's integral, its element type type is changed to `float`.
156// Returns min/max floating-point values unchanged.
157template <UnitEnum E, typename T>
158[[nodiscard]] detail::Units::MakeFloatingPoint<T> convertUnits( E from, E to, const T& value )
159{
160 using ReturnType = detail::Units::MakeFloatingPoint<T>;
161
162 bool needConversion = !unitsAreEquivalent( from, to );
163
164 if constexpr ( std::is_same_v<T, ReturnType> )
165 {
166 if ( !needConversion )
167 return value;
168 }
169
170 ReturnType ret{};
171
172 for ( int i = 0; i < VectorTraits<T>::size; i++ )
173 {
175
176 // Don't touch min/max floating-point values.
177 bool needElemConversion = needConversion;
178 if constexpr ( std::is_floating_point_v<typename VectorTraits<T>::BaseType> )
179 {
180 if ( needElemConversion &&
181 (
182 target <= std::numeric_limits<typename VectorTraits<T>::BaseType>::lowest() ||
183 target >= std::numeric_limits<typename VectorTraits<T>::BaseType>::max()
184 )
185 )
186 needElemConversion = false;
187 }
188
189 if ( needElemConversion )
190 target = target * getUnitInfo( from ).conversionFactor / getUnitInfo( to ).conversionFactor;
191 }
192
193 return ret;
194}
195
196// ---
197
198template <UnitEnum E>
199struct UnitToStringParams;
200
201// Returns the default parameters for converting a specific unit type to a string.
202// You can modify those with `setDefaultUnitParams()`.
203template <UnitEnum E>
205
206#define MR_X(E) extern template MRVIEWER_API const UnitToStringParams<E>& getDefaultUnitParams();
208#undef MR_X
209
210// Modifies the default parameters for converting a specific unit type to a string.
211template <UnitEnum E>
213
214#define MR_X(E) extern template MRVIEWER_API void setDefaultUnitParams( const UnitToStringParams<E>& newParams );
216#undef MR_X
217
218enum class NumberStyle
219{
220 normal, // Like %f.
221 distributePrecision, // Like %f, but the precision digits are spread across both decimal and integral parts.
222 exponential, // Like %e.
223 maybeExponential, // Like %g.
224};
225
226// This controls how the degrees are printed.
227enum class DegreesMode
228{
229 degrees, // Fractional degrees.
230 degreesMinutes, // Integral degrees, fractional arcminutes.
231 degreesMinutesSeconds, // Integral degrees and minutes, fractional arcseconds.
232};
233
234// Controls how a value with a unit is converted to a string.
235template <UnitEnum E>
237{
238 // The resulting string is wrapped in this.
239 // Do NOT use this for custom unit suffixes! Add them as actual units instead.
240 std::string_view decorationFormatString = "{}";
241
242 // --- Units:
243
244 // The measurement unit of the input.
245 // If null, assumed to be the same as `targetUnit`, and no conversion is performed.
246 // If not null, the value is converted from this unit to `targetUnit`.
248
249 // The measurement unit of the result.
251
252 // Whether to show the unit suffix.
254
255 // --- Precision:
256
257 // The output style. (Scientific notation or not, fixed-precision or not.)
259
260 // How many digits of precision.
262
263 // --- Other:
264
265 // If false, silently remove the minus sign before negative zeroes
266 // (including numbers that get rounded into negative zeroes with the current `precision` setting).
267 bool allowNegativeZero = getDefaultUnitParams<E>().allowNegativeZero;
268
269 // Use a pretty Unicode minus sign instead of the ASCII `-`.
270 bool unicodeMinusSign = getDefaultUnitParams<E>().unicodeMinusSign;
271
272 // If non-zero, this character is inserted between every three digits to the left of the decimal point.
273 char thousandsSeparator = getDefaultUnitParams<E>().thousandsSeparator;
274 // If non-zero, this character is inserted between every three digits to the right of the decimal point.
275 char thousandsSeparatorFrac = getDefaultUnitParams<E>().thousandsSeparatorFrac;
276
277 // If false, remove zero before the fractional point (`.5` instead of `0.5`).
279
280 // Remove trailing zeroes after the fractional point. If the point becomes the last symbol, remove the point too.
281 bool stripTrailingZeroes = getDefaultUnitParams<E>().stripTrailingZeroes;
282
283 // When printing degrees, this lets you display arcminutes and possibly arcseconds. Ignored for everything else.
284 std::conditional_t<std::is_same_v<E, AngleUnit>, DegreesMode, detail::Units::Empty> degreesMode = getDefaultUnitParams<E>().degreesMode;
285
286 // If you add new fields there, update the initializer for `defaultUnitToStringParams` in `MRUnits.cpp`.
287
288 friend bool operator==( const UnitToStringParams&, const UnitToStringParams& ) = default;
289};
290
291// The `std::variant` of `UnitToStringParams<E>` for all known `E`s (unit kinds).
292using VarUnitToStringParams = std::variant<
293 #define MR_TRIM_LEADING_COMMA(...) MR_TRIM_LEADING_COMMA_(__VA_ARGS__)
294 #define MR_TRIM_LEADING_COMMA_(x, ...) __VA_ARGS__
295 #define MR_X(E) , UnitToStringParams<E>
297 #undef MR_TRIM_LEADING_COMMA
298 #undef MR_TRIM_LEADING_COMMA_
299 #undef MR_X
300>;
301
302// Converts value to a string, possibly converting it to a different unit.
303// By default, length is kept as is, while angles are converted from radians to the current UI unit.
304template <UnitEnum E, detail::Units::Scalar T>
305[[nodiscard]] MRVIEWER_API std::string valueToString( T value, const UnitToStringParams<E>& params = getDefaultUnitParams<E>() );
306
307#define MR_Y(T, E) extern template MRVIEWER_API std::string valueToString<E, T>( T value, const UnitToStringParams<E>& params );
308#define MR_X(E) DETAIL_MR_UNIT_VALUE_TYPES(MR_Y, E)
310#undef MR_X
311#undef MR_Y
312
313// This overload lets you select the unit kind at runtime.
314template <detail::Units::Scalar T>
315[[nodiscard]] MRVIEWER_API std::string valueToString( T value, const VarUnitToStringParams& params );
316
317#define MR_X(T) extern template MRVIEWER_API std::string valueToString( T value, const VarUnitToStringParams& params );
319#undef MR_X
320
321// Guesses the number of digits of precision for fixed-point formatting of `value`.
322// Mostly for internal use.
323template <detail::Units::Scalar T>
324[[nodiscard]] MRVIEWER_API int guessPrecision( T value );
325
326// Guesses the number of digits of precision for fixed-point formatting of the min-max range.
327// If `min >= max`, always returns zero. Ignores min and/or max if they are the smallest of the largest representable value respectively.
328// Mostly for internal use.
329template <detail::Units::Scalar T>
330[[nodiscard]] MRVIEWER_API int guessPrecision( T min, T max );
331
332// Same but for vectors.
333template <typename T>
335[[nodiscard]] int guessPrecision( T value )
336{
337 int ret = 0;
338 for ( int i = 0; i < VectorTraits<T>::size; i++ )
339 ret = std::max( ret, guessPrecision( VectorTraits<T>::getElem( i, value ) ) );
340 return ret;
341}
342template <typename T>
343requires (VectorTraits<T>::size > 1 && detail::Units::Scalar<typename VectorTraits<T>::BaseType>)
344[[nodiscard]] int guessPrecision( T min, T max )
345{
346 int ret = 0;
347 for ( int i = 0; i < VectorTraits<T>::size; i++ )
348 ret = std::max( ret, guessPrecision( VectorTraits<T>::getElem( i, min ), VectorTraits<T>::getElem( i, max ) ) );
349 return ret;
350}
351
352#define MR_X(T) \
353 extern template MRVIEWER_API int guessPrecision( T value ); \
354 extern template MRVIEWER_API int guessPrecision( T min, T max );
356#undef MR_X
357
358// Generates a printf-style format string for `value`, for use with ImGui widgets.
359// It has form "123.45 mm##%.6f" (the baked number, then `##` and some format string).
360// The `##...` part isn't printed, but we need it when ctrl+clicking the number, to show the correct number of digits.
361template <UnitEnum E, detail::Units::Scalar T>
362[[nodiscard]] MRVIEWER_API std::string valueToImGuiFormatString( T value, const UnitToStringParams<E>& params = getDefaultUnitParams<E>() );
363
364#define MR_Y(T, E) extern template MRVIEWER_API std::string valueToImGuiFormatString( T value, const UnitToStringParams<E>& params );
365#define MR_X(E) DETAIL_MR_UNIT_VALUE_TYPES(MR_Y, E)
367#undef MR_X
368#undef MR_Y
369
370// This overload lets you select the unit kind at runtime.
371template <detail::Units::Scalar T>
372[[nodiscard]] MRVIEWER_API std::string valueToImGuiFormatString( T value, const VarUnitToStringParams& params );
373
374#define MR_X(T) extern template MRVIEWER_API std::string valueToImGuiFormatString( T value, const VarUnitToStringParams& params );
376#undef MR_X
377
378}
#define MR_X(E)
Definition MRUnits.h:132
#define MR_TRIM_LEADING_COMMA(...)
#define DETAIL_MR_UNIT_VALUE_TYPES(X,...)
Definition MRUnits.h:97
#define DETAIL_MR_UNIT_ENUMS(X)
Definition MRUnits.h:94
Definition MRUnits.h:107
Definition MRUnits.h:148
std::conditional_t< std::is_integral_v< typename VectorTraits< T >::BaseType >, typename VectorTraits< T >::template ChangeBaseType< float >, T > MakeFloatingPoint
Definition MRUnits.h:151
Definition MRCameraOrientationPlugin.h:7
PixelSizeUnit
Definition MRUnits.h:40
NumberStyle
Definition MRUnits.h:219
MRVIEWER_API int guessPrecision(T value)
Definition MRUnits.h:335
MRVIEWER_API std::string valueToString(T value, const UnitToStringParams< E > &params=getDefaultUnitParams< E >())
const UnitInfo & getUnitInfo(E unit)=delete
NoUnit
Definition MRUnits.h:18
bool unitsAreEquivalent(E a, E b)
Definition MRUnits.h:138
LengthUnit
Definition MRUnits.h:24
const UnitToStringParams< E > & getDefaultUnitParams()
void setDefaultUnitParams(const UnitToStringParams< E > &newParams)
MRVIEWER_API std::string valueToImGuiFormatString(T value, const UnitToStringParams< E > &params=getDefaultUnitParams< E >())
RatioUnit
Definition MRUnits.h:47
DegreesMode
Definition MRUnits.h:228
MovementSpeedUnit
Definition MRUnits.h:63
detail::Units::MakeFloatingPoint< T > convertUnits(E from, E to, const T &value)
Definition MRUnits.h:158
VolumeUnit
Definition MRUnits.h:79
AreaUnit
Definition MRUnits.h:71
TimeUnit
Definition MRUnits.h:55
InvLengthUnit
Definition MRUnits.h:87
AngleUnit
Definition MRUnits.h:32
std::variant< > VarUnitToStringParams
Definition MRUnits.h:292
Definition MRUnits.h:116
std::string_view unitSuffix
Definition MRUnits.h:125
std::string_view prettyName
Definition MRUnits.h:121
float conversionFactor
Definition MRUnits.h:119
Definition MRUnits.h:237
bool leadingZero
Definition MRUnits.h:278
int precision
Definition MRUnits.h:261
std::conditional_t< std::is_same_v< E, AngleUnit >, DegreesMode, detail::Units::Empty > degreesMode
Definition MRUnits.h:284
NumberStyle style
Definition MRUnits.h:258
std::optional< E > sourceUnit
Definition MRUnits.h:247
std::string_view decorationFormatString
Definition MRUnits.h:240
char thousandsSeparator
Definition MRUnits.h:273
bool unitSuffix
Definition MRUnits.h:253
bool allowNegativeZero
Definition MRUnits.h:267
E targetUnit
Definition MRUnits.h:250
char thousandsSeparatorFrac
Definition MRUnits.h:275
friend bool operator==(const UnitToStringParams &, const UnitToStringParams &)=default
bool stripTrailingZeroes
Definition MRUnits.h:281
bool unicodeMinusSign
Definition MRUnits.h:270
Definition MRMesh/MRVectorTraits.h:14
static constexpr int size
Definition MRMesh/MRVectorTraits.h:18
static constexpr auto && getElem(int i, U &&value)
Definition MRMesh/MRVectorTraits.h:27
T BaseType
Definition MRMesh/MRVectorTraits.h:17
Definition MRUnits.h:145