Skip to content

Commit 5a0e350

Browse files
committed
feat: lerp and midpoint for points added
1 parent f5f502e commit 5a0e350

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed

src/core/include/mp-units/math.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,48 @@ template<auto R1, typename Rep1, auto R2, typename Rep2, auto R3, typename Rep3>
454454
return quantity{hypot(x.numerical_value_in(unit), y.numerical_value_in(unit), z.numerical_value_in(unit)), ref};
455455
}
456456

457+
/**
458+
* @brief Linear interpolation or extrapolation
459+
*
460+
* Computes the linear interpolation between `a` and `b`, if the parameter `t` is inside `[​0​, 1)`
461+
* (the linear extrapolation otherwise), i.e. the result of `a + t(b − a)` with accounting for
462+
* floating-point calculation imprecision.
463+
*/
464+
template<auto R1, auto Origin, typename Rep1, auto R2, typename Rep2, typename Factor>
465+
requires requires(Rep1 a, Rep2 b, Factor t) {
466+
get_common_reference(R1, R2);
467+
requires requires { lerp(a, b, t); } || requires { std::lerp(a, b, t); };
468+
}
469+
[[nodiscard]] constexpr QuantityPointOf<get_quantity_spec(get_common_reference(R1, R2))> auto lerp(
470+
const quantity_point<R1, Origin, Rep1>& a, const quantity_point<R2, Origin, Rep2>& b, const Factor& t) noexcept
471+
{
472+
constexpr auto ref = get_common_reference(R1, R2);
473+
constexpr auto unit = get_unit(ref);
474+
using std::lerp;
475+
return Origin + quantity{lerp(a.quantity_ref_from(Origin).numerical_value_in(unit),
476+
b.quantity_ref_from(Origin).numerical_value_in(unit), t),
477+
ref};
478+
}
479+
480+
/**
481+
* @brief Computes the midpoint of two points
482+
*/
483+
template<auto R1, auto Origin, typename Rep1, auto R2, typename Rep2>
484+
requires requires(Rep1 a, Rep2 b) {
485+
get_common_reference(R1, R2);
486+
requires requires { midpoint(a, b); } || requires { std::midpoint(a, b); };
487+
}
488+
[[nodiscard]] constexpr QuantityPointOf<get_quantity_spec(get_common_reference(R1, R2))> auto midpoint(
489+
const quantity_point<R1, Origin, Rep1>& a, const quantity_point<R2, Origin, Rep2>& b) noexcept
490+
{
491+
constexpr auto ref = get_common_reference(R1, R2);
492+
constexpr auto unit = get_unit(ref);
493+
using std::midpoint;
494+
return Origin + quantity{midpoint(a.quantity_ref_from(Origin).numerical_value_in(unit),
495+
b.quantity_ref_from(Origin).numerical_value_in(unit)),
496+
ref};
497+
}
498+
457499
#endif // MP_UNITS_HOSTED
458500

459501
} // namespace mp_units

test/runtime/math_test.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ import mp_units;
4141
using namespace mp_units;
4242
using namespace mp_units::si::unit_symbols;
4343

44+
inline constexpr struct mean_sea_level final : mp_units::absolute_point_origin<mp_units::isq::altitude> {
45+
} mean_sea_level;
46+
4447
// classical
4548

4649
TEST_CASE("math operations", "[math]")
@@ -353,6 +356,80 @@ TEST_CASE("math operations", "[math]")
353356
}
354357
}
355358

359+
SECTION("lerp functions")
360+
{
361+
SECTION("lerp should work on the same quantity points")
362+
{
363+
SECTION("default origins")
364+
{
365+
REQUIRE(lerp(point<isq::altitude[m]>(99.), point<isq::altitude[m]>(100.), 0.0) == point<isq::altitude[m]>(99.));
366+
REQUIRE(lerp(point<isq::altitude[m]>(99.), point<isq::altitude[m]>(100.), 0.5) ==
367+
point<isq::altitude[m]>(99.5));
368+
REQUIRE(lerp(point<isq::altitude[m]>(99.), point<isq::altitude[m]>(100.), 1.0) ==
369+
point<isq::altitude[m]>(100.));
370+
REQUIRE(lerp(point<isq::altitude[m]>(99.), point<isq::altitude[m]>(100.), 2.0) ==
371+
point<isq::altitude[m]>(101.));
372+
}
373+
374+
SECTION("custom origins")
375+
{
376+
REQUIRE(lerp(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(100. * m), 0.0) ==
377+
mean_sea_level + isq::height(99. * m));
378+
REQUIRE(lerp(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(100. * m), 0.5) ==
379+
mean_sea_level + isq::height(99.5 * m));
380+
REQUIRE(lerp(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(100. * m), 1.0) ==
381+
mean_sea_level + isq::height(100. * m));
382+
REQUIRE(lerp(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(100. * m), 2.0) ==
383+
mean_sea_level + isq::height(101. * m));
384+
}
385+
}
386+
387+
SECTION("lerp should work with different units of the same dimension")
388+
{
389+
SECTION("default origins")
390+
{
391+
REQUIRE(lerp(point<isq::altitude[m]>(99.), point<isq::altitude[cm]>(10'000.), 0.0) ==
392+
point<isq::altitude[m]>(99.));
393+
REQUIRE(lerp(point<isq::altitude[m]>(99.), point<isq::altitude[cm]>(10'000.), 0.5) ==
394+
point<isq::altitude[m]>(99.5));
395+
REQUIRE(lerp(point<isq::altitude[m]>(99.), point<isq::altitude[cm]>(10'000.), 1.0) ==
396+
point<isq::altitude[m]>(100.));
397+
REQUIRE(lerp(point<isq::altitude[m]>(99.), point<isq::altitude[cm]>(10'000.), 2.0) ==
398+
point<isq::altitude[m]>(101.));
399+
}
400+
401+
SECTION("custom origins")
402+
{
403+
REQUIRE(lerp(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(10'000. * cm), 0.0) ==
404+
mean_sea_level + isq::height(99. * m));
405+
REQUIRE(lerp(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(10'000. * cm), 0.5) ==
406+
mean_sea_level + isq::height(99.5 * m));
407+
REQUIRE(lerp(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(10'000. * cm), 1.0) ==
408+
mean_sea_level + isq::height(100. * m));
409+
REQUIRE(lerp(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(10'000. * cm), 2.0) ==
410+
mean_sea_level + isq::height(101. * m));
411+
}
412+
}
413+
}
414+
415+
SECTION("midpoint functions")
416+
{
417+
SECTION("midpoint should work on the same quantity points")
418+
{
419+
REQUIRE(midpoint(point<isq::altitude[m]>(99.), point<isq::altitude[m]>(100.)) == point<isq::altitude[m]>(99.5));
420+
REQUIRE(midpoint(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(100. * m)) ==
421+
mean_sea_level + isq::height(99.5 * m));
422+
}
423+
424+
SECTION("midpoint should work with different units of the same dimension")
425+
{
426+
REQUIRE(midpoint(point<isq::altitude[m]>(99.), point<isq::altitude[cm]>(10'000.)) ==
427+
point<isq::altitude[m]>(99.5));
428+
REQUIRE(midpoint(mean_sea_level + isq::height(99. * m), mean_sea_level + isq::height(10'000. * cm)) ==
429+
mean_sea_level + isq::height(99.5 * m));
430+
}
431+
}
432+
356433
SECTION("SI trigonometric functions")
357434
{
358435
SECTION("sin")

0 commit comments

Comments
 (0)