diff --git a/.gitmodules b/.gitmodules index 64de243d4..a42b499aa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -36,3 +36,6 @@ [submodule "submodules/bear"] path = submodules/bear url = https://github.com/ebu/bear.git +[submodule "submodules/adm_coordinate_conversion"] + path = submodules/adm_coordinate_conversion + url = https://github.com/ebu/adm_coordinate_conversion.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 136c96b3e..cbf38ce68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Note: This release bumps the minimum required REAPER version from v6.11 to v6.37 * Fix occasional crash bug in start up of Binaural Monitoring plugin [#277](https://github.com/ebu/ear-production-suite/issues/277) [#278](https://github.com/ebu/ear-production-suite/pull/278) * Fix fail to render when non-VST plugins are in FX chains [#279](https://github.com/ebu/ear-production-suite/issues/279) [#280](https://github.com/ebu/ear-production-suite/pull/280) * Update BEAR [#287](https://github.com/ebu/ear-production-suite/pull/287) +* Use ADM Coordinate Conversion lib over internal implementation [#293](https://github.com/ebu/ear-production-suite/issues/293) [#295](https://github.com/ebu/ear-production-suite/pull/295) Version 1.1.0b diff --git a/reaper-adm-extension/src/reaper_adm/CMakeLists.txt b/reaper-adm-extension/src/reaper_adm/CMakeLists.txt index e4a1ffd85..08142d3d9 100644 --- a/reaper-adm-extension/src/reaper_adm/CMakeLists.txt +++ b/reaper-adm-extension/src/reaper_adm/CMakeLists.txt @@ -70,7 +70,6 @@ set(EXTENSION_SOURCES reaperhost.cpp track.cpp update_check.cpp - coordinate_conversion/coord_conv.cpp progress/importdialog.cpp progress/importlistener.cpp progress/importprogress.cpp @@ -155,7 +154,6 @@ set(EXTENSION_HEADERS track.h update_check.h win_mem_debug.h - coordinate_conversion/coord_conv.hpp progress/importdialog.h progress/importlistener.h progress/importprogress.h @@ -201,6 +199,7 @@ target_include_directories(reaper_adm_object $ $ $ + $ $ $ ${EPS_SHARED_DIR} @@ -211,6 +210,7 @@ target_link_libraries(reaper_adm_dependencies $ IRT::bw64 adm + AdmCoordConv WDL::swell Boost::filesystem nng::nng diff --git a/reaper-adm-extension/src/reaper_adm/admextraction.cpp b/reaper-adm-extension/src/reaper_adm/admextraction.cpp index 1115f8372..0034c873a 100644 --- a/reaper-adm-extension/src/reaper_adm/admextraction.cpp +++ b/reaper-adm-extension/src/reaper_adm/admextraction.cpp @@ -1,4 +1,4 @@ -#include "coordinate_conversion/coord_conv.hpp" +#include #include "admextraction.h" using namespace admplug::detail; @@ -22,16 +22,38 @@ bool RoomCartesianToSpherical::hasExtent() { block.has(); } + void RoomCartesianToSpherical::calculate() { if(!position && block.has()) { - auto inputPosition = block.get(); - if(hasExtent()) { - auto inputExtent = std::make_tuple(getValueOrDefault(), - getValueOrDefault(), - getValueOrDefault()); - std::tie(position, extent) = adm::cartToPolar(std::make_tuple(inputPosition, inputExtent)); - } else { - position = adm::pointCartToPolar(inputPosition); + + auto admInputPosition = block.get(); + adm::coords::CartesianSource inputSource{ { + admInputPosition.get().get(), + admInputPosition.get().get(), + admInputPosition.get().get() + } }; + + if (hasExtent()) { + inputSource.extent = adm::coords::CartesianExtent{ + getValueOrDefault().get(), + getValueOrDefault().get(), + getValueOrDefault().get() + }; + } + + auto convSource = adm::coords::convert(inputSource); + position = adm::SphericalPosition( + adm::Azimuth(convSource.position.azimuth), + adm::Elevation(convSource.position.elevation), + adm::Distance(convSource.position.distance) + ); + + if (convSource.extent.has_value()) { + extent = std::make_tuple( + adm::Width(convSource.extent->width), + adm::Height(convSource.extent->height), + adm::Depth(convSource.extent->depth) + ); } } } @@ -59,8 +81,20 @@ RoomCartesianToSphericalSpeaker::RoomCartesianToSphericalSpeaker( void RoomCartesianToSphericalSpeaker::calculate() { if(!position) { - auto inputPosition = block.get(); - position = adm::pointCartToPolar(inputPosition); + + auto admInputPosition = block.get(); + adm::coords::CartesianPosition inputPosition{ + getParamValueOrZero(admInputPosition), + getParamValueOrZero(admInputPosition), + getParamValueOrZero(admInputPosition) + }; + + auto convPosition = adm::coords::convert(inputPosition); + position = adm::SphericalSpeakerPosition( + adm::Azimuth(convPosition.azimuth), + adm::Elevation(convPosition.elevation), + adm::Distance(convPosition.distance) + ); } } diff --git a/reaper-adm-extension/src/reaper_adm/admextraction.h b/reaper-adm-extension/src/reaper_adm/admextraction.h index 7c162f792..639da3489 100644 --- a/reaper-adm-extension/src/reaper_adm/admextraction.h +++ b/reaper-adm-extension/src/reaper_adm/admextraction.h @@ -63,6 +63,14 @@ auto getAdmComponent(adm::AudioBlockFormatDirectSpeakers const& block) { return getOptionalAdmComponent(block); } +template +float getParamValueOrZero(T const& element) { + if (element.template has()) { + return element.template get().get(); + } + return 0; +} + using ns = std::chrono::nanoseconds; template diff --git a/reaper-adm-extension/src/reaper_adm/coordinate_conversion/coord_conv.cpp b/reaper-adm-extension/src/reaper_adm/coordinate_conversion/coord_conv.cpp deleted file mode 100644 index 1d55bd5ec..000000000 --- a/reaper-adm-extension/src/reaper_adm/coordinate_conversion/coord_conv.cpp +++ /dev/null @@ -1,466 +0,0 @@ -#include "coord_conv.hpp" -#include -#include -#include - -namespace { - template - float getValueOrZero(T const& element) { - if(element.template has()) { - return element.template get().get(); - } - return 0; - } - - struct AzCartMapping { - double az; - double x; - double y; - double z; - }; - - struct Cartesian { - double x; - double y; - double z; - }; - - Cartesian operator*(Cartesian const& lhs, double rhs) { - return {lhs.x * rhs, lhs.y * rhs, lhs.z * rhs}; - } - - Cartesian operator*(double lhs, Cartesian const& rhs) { - return rhs * lhs; - } - - struct Polar { - double az; - double el; - double d; - }; - - struct ElevationSpec { - double top{30.0}; - double top_tilde{45.0}; - }; - - std::array mapping {{{0, 0, 1, 0}, - {-30, 1, 1, 0}, - {-110, 1, -1, 0}, - {110, -1, -1, 0}, - {30, -1, 1, 0}}}; - - constexpr double pi = 3.14159265358979323846; - - constexpr double radians(double degrees) { - return degrees * pi / 180.0; - } - - constexpr double degrees(double radians) { - return radians * 180.0 / pi; - } - - struct CartesianExtent { - double x; - double y; - double z; - }; - - struct PolarExtent { - double width; - double height; - double depth; - }; - - - - bool insideAngleRange(double x, double start, double end, double tol = 0.0) { - while((end - 360.0) > start) { - end -= 360.0; - } - while(end < start) { - end += 360.0; - } - auto start_tol = start - tol; - while ((x - 360.0) >= start_tol) { - x -= 360.0; - } - while (x < start_tol) { - x += 360.0; - } - return x <= end + tol; - } - - std::pair findSector(double az) { - for(auto i = 0u; i != mapping.size(); ++i) { - auto j = (i + 1) % mapping.size(); - if(insideAngleRange(az, mapping[j].az, mapping[i].az)) { - return {mapping[i], mapping[j]}; - } - } - throw std::runtime_error("azimuth not found in any sector"); - } - - double toAzimuth(double x, double y, double z) { - return -(degrees(std::atan2(x, y))); - } - - std::pair findCartSector(double az) { - for(auto i = 0u; i != mapping.size(); ++i) { - auto j = (i + 1) % mapping.size(); - if(insideAngleRange(az, - toAzimuth(mapping[j].x, mapping[j].y, mapping[j].z), - toAzimuth(mapping[i].x, mapping[i].y, mapping[i].z))) { - return {mapping[i], mapping[j]}; - } - } - throw std::runtime_error("azimuth not found in any sector"); - } - - double relativeAngle(double x, double y) { - while ((y - 360.0) >= x) { - y -= 360.0; - } - while (y < x) { - y += 360.0; - } - return y; - } - - double mapAzToLinear(double left_az, double right_az, double azimuth) { - auto mid_az = (left_az + right_az) / 2.0; - auto az_range = right_az - mid_az; - auto rel_az = azimuth - mid_az; - auto gain_r = 0.5 + 0.5 * std::tan(radians(rel_az)) / tan(radians(az_range)); - return std::atan2(gain_r, 1-gain_r) * (2 / pi); - } - - double mapLinearToAz(double left_az, double right_az, double x) { - auto mid_az = (left_az + right_az) / 2.0; - auto az_range = right_az - mid_az; - auto gain_l_= std::cos(x * pi / 2.0); - auto gain_r_ = std::sin(x * pi / 2.0); - auto gain_r = gain_r_ / (gain_l_ + gain_r_); - auto rel_az = degrees(std::atan(2.0 * (gain_r - 0.5) * std::tan(radians(az_range)))); - return mid_az + rel_az; - } - - std::pair calculateXY(double az, double r_xy) { - AzCartMapping leftSector{}; - AzCartMapping rightSector{}; - std::tie(leftSector, rightSector) = findSector(az); - auto relAz = relativeAngle(rightSector.az, az); - auto relLeftAz = relativeAngle(rightSector.az, leftSector.az); - auto p = mapAzToLinear(relLeftAz, rightSector.az, relAz); - auto x = r_xy * (leftSector.x + (rightSector.x - leftSector.x) * p); - auto y = r_xy * (leftSector.y + (rightSector.y - leftSector.y) * p); - return {x, y}; - } - - std::pair calcZ_r_xy(Polar const& polar, ElevationSpec const& elSpec) { - double z{}, r_xy{}; - if (std::abs(polar.el) > elSpec.top) { - auto el_tilde = elSpec.top_tilde + (90.0 - elSpec.top_tilde) * (std::abs(polar.el) - elSpec.top) / (90.0 - elSpec.top); - z = std::copysign(polar.d, polar.el); - r_xy = polar.d * std::tan(radians(90.0 - el_tilde)); - } else { - auto el_tilde = elSpec.top_tilde * (polar.el / elSpec.top); - z = tan(radians(el_tilde)) * polar.d; - r_xy = polar.d; - } - return {z, r_xy}; - } - - Cartesian pointPolarToCart(Polar const& polar) { - Cartesian cart{}; - double r_xy{}; - std::tie(cart.z, r_xy) = calcZ_r_xy(polar, ElevationSpec()); - std::tie(cart.x, cart.y) = calculateXY(polar.az, r_xy); - return cart; - } - - bool xAndYNearZero(Cartesian const& cart, double epsilon) { - return (std::abs(cart.x) < epsilon && std::abs(cart.y) < epsilon); - } - - bool zNearZero(Cartesian const& cart, double epsilon) { - return std::abs(cart.z) < epsilon; - } - - boost::optional nearZeroConversion(Cartesian const& cart) { - double const epsilon{1e-10}; - if (xAndYNearZero(cart, epsilon)) { - if (zNearZero(cart, epsilon)) { - return {{0, 0, 0}}; - } else { - return {{0.0, std::copysign(90, cart.z), std::abs(cart.z)}}; - } - } - return {}; - } - - std::array invert(std::array const& matrix2by2) { - auto det = (matrix2by2[0] * matrix2by2[3] - matrix2by2[1] * matrix2by2[2]); - auto mul = 1.0 / det; - auto inv = std::array {matrix2by2[3] * mul, - matrix2by2[1] * mul, - -matrix2by2[2] * mul, matrix2by2[0] * mul}; - return inv; - } - - std::array dotProduct(std::array const& rowVec, std::array const& matrix2by2) { - return {rowVec[0] * matrix2by2[0] + rowVec[1] * matrix2by2[2], - rowVec[0] * matrix2by2[1] + rowVec[1] * matrix2by2[3]}; - } - - // didn't want to add a dependency to do one linear algebra calc - std::pair calculate_g_lr(Cartesian const& cart, std::pair const& sectors) { - auto const& left = sectors.first; - auto const& right = sectors.second; - auto inverse = invert({left.x, left.y, right.x, right.y}); - auto dot = dotProduct({cart.x, cart.y}, inverse); - return {dot[0], dot[1]}; - } - - Polar convert(Cartesian const& cart, - ElevationSpec elSpec) { - auto sectors = findCartSector(toAzimuth(cart.x, cart.y, 0)); - auto const& left = sectors.first; - auto const& right = sectors.second; - auto rel_left_az = relativeAngle(right.az, left.az); - auto g_lr = calculate_g_lr(cart, sectors); - auto r_xy = g_lr.first + g_lr.second; - Polar polar{}; - auto relAz = mapLinearToAz(rel_left_az, right.az, g_lr.second / r_xy); - polar.az = relativeAngle(-180, relAz); - auto el_tilde = degrees(std::atan(cart.z / r_xy)); - - if(std::abs(el_tilde) > elSpec.top_tilde) { - auto abs_el = elSpec.top + ((90.0 - elSpec.top) * (std::abs(el_tilde) - elSpec.top_tilde)) - / (90.0 - elSpec.top_tilde); - polar.el = std::copysign(abs_el, el_tilde); - polar.d = std::abs(cart.z); - } else { - polar.el = elSpec.top * el_tilde / elSpec.top_tilde; - polar.d = r_xy; - } - return polar; - } - - Polar pointCartToPolar(Cartesian const& cart) { - auto converted = nearZeroConversion(cart); - if (converted) { - return *converted; - } else { - return convert(cart, ElevationSpec()); - } - } - - - // Converts a polar extent value into a cartesian extent assuming position in directly in front of listener - // and a radius of 1. See BS.2127, section 10.2 - CartesianExtent whd2xyz(PolarExtent const& polarExtent) { - double x_size_width{}, y_size_width{}, z_size_height{}, y_size_height{}, y_size_depth{}; - if(polarExtent.width < 180.0) { - x_size_width = std::sin(radians(polarExtent.width / 2.0)); - } else { - x_size_width = 1.0; - } - y_size_width = (1.0 - std::cos(radians(polarExtent.width / 2.0))) / 2.0; - - if(polarExtent.height < 180.0) { - z_size_height = std::sin(radians(polarExtent.height / 2.0)); - } else { - z_size_height = 1.0; - } - y_size_height = (1.0 - std::cos(radians(polarExtent.height / 2.0))) / 2.0; - y_size_depth = polarExtent.depth; - return {x_size_width, std::max(y_size_width, std::max(y_size_height, y_size_depth)), z_size_height}; - } - - PolarExtent xyz2whd(CartesianExtent const& cartExtent) { - PolarExtent polarExtent{}; - auto width_from_sx = 2.0 * degrees(std::asin(cartExtent.x)); - auto width_from_sy = 2.0 * degrees(std::acos(1.0 - (2 * cartExtent.y))); - polarExtent.width = width_from_sx + cartExtent.x * std::max(width_from_sy - width_from_sx, 0.0); - - auto height_from_sz = 2.0 * degrees(std::asin(cartExtent.z)); - auto height_from_sy = 2.0 * degrees(std::acos(1.0 - (2.0 * cartExtent.y))); - polarExtent.height = height_from_sz + cartExtent.z * std::max(height_from_sy - height_from_sz, 0.0); - - auto equiv_y = whd2xyz({polarExtent.width, polarExtent.height, 0.0}).y; - polarExtent.depth = std::max(0.0, cartExtent.y - equiv_y); - return polarExtent; - } - - double euclidianNorm(double a, double b, double c) { - return std::sqrt(a * a + b * b + c * c); - } - - Cartesian toCart(Polar const& polar) { - Cartesian cart{}; - cart.x = std::sin(-pi * polar.az / 180.0) * std::cos(pi * polar.el / 180.0) * polar.d; - cart.y = std::cos(-pi * polar.az / 180.0) * std::cos(pi * polar.el / 180.0) * polar.d; - cart.z = std::sin(pi * polar.el / 180.0) * polar.d; - return cart; - } - - // returns a rotation matrix that maps a forward vector to a given azimuth an elevation - // See BS.2127, section 10.2 - std::array localCoordinateSystem(double az, double el) { - return {toCart({az - 90.0, 0, 1}), - toCart({az, el, 1}), - toCart({az, el + 90.0, 1})}; - } - - std::array transpose(std::array const& mat) { - return{Cartesian{mat[0].x, mat[1].x, mat[2].x}, - Cartesian{mat[0].y, mat[1].y, mat[2].y}, - Cartesian{mat[0].z, mat[1].z, mat[2].z}}; - } - - std::array calcMPolarToCart(CartesianExtent const& forwardExtent, Polar const& position) { - std::array M{}; - auto rotation = localCoordinateSystem(position.az, position.el); - M[0] = {forwardExtent.x * rotation[0]}; - M[1] = {forwardExtent.y * rotation[1]}; - M[2] = {forwardExtent.z * rotation[2]}; - return M; - } - - std::array calcMCartToPolar(CartesianExtent const& forwardExtent, Polar const& position) { - std::array M{}; - auto rotation = transpose(localCoordinateSystem(position.az, position.el)); - M[0] = {forwardExtent.x * rotation[0]}; - M[1] = {forwardExtent.y * rotation[1]}; - M[2] = {forwardExtent.z * rotation[2]}; - return M; - } - - CartesianExtent polarToCartExtent(Polar const & polarPosition, PolarExtent const& polarExtent) { - auto cartesianForwardExtent = whd2xyz(polarExtent); - auto M = calcMPolarToCart(cartesianForwardExtent, polarPosition); - return {euclidianNorm(M[0].x, M[1].x, M[2].x), - euclidianNorm(M[0].y, M[1].y, M[2].y), - euclidianNorm(M[0].z, M[1].z, M[2].z)}; - } - - PolarExtent cartToPolarExtent(Polar const& polarPosition, CartesianExtent const& cartExtent) { - auto M = calcMCartToPolar(cartExtent, polarPosition); - auto cartForwardExtent = CartesianExtent{euclidianNorm(M[0].x, M[1].x, M[2].x), - euclidianNorm(M[0].y, M[1].y, M[2].y), - euclidianNorm(M[0].z, M[1].z, M[2].z)}; - return xyz2whd(cartForwardExtent); - } -} - -namespace adm { - namespace { - PolarExtent extentFromTuple(std::tuple const &admExtent) { - return {std::get<0>(admExtent).get(), - std::get<1>(admExtent).get(), - std::get<2>(admExtent).get()}; - } - - ::Polar toPolarFromAdm(std::tuple const &point) { - return {std::get<0>(point).get(), - std::get<1>(point).get(), - std::get<2>(point).get()}; - } - - ::Polar toPolarFromAdm(SphericalPosition const &admPosition) { - return {toPolarFromAdm(std::make_tuple( - admPosition.get(), - admPosition.get(), - admPosition.get()))}; - } - - adm::SphericalPosition toAdmFromPolar(::Polar const &point) { - return {Azimuth{static_cast(point.az)}, - Elevation{static_cast(point.el)}, - Distance{static_cast(point.d)}}; - } - - adm::Extent toAdmFromPolar(::PolarExtent const &extent) { - return {Width{static_cast(extent.width)}, - Height{static_cast(extent.height)}, - Depth{static_cast(extent.depth)}}; - } - } - - void pointPolarToCart(double azimuth, double elevation, double distance, double &x, double &y, double &z) { - auto cart = pointPolarToCart(Polar{azimuth, elevation, distance}); - std::tie(x, y, z) = std::tie(cart.x, cart.y, cart.z); - } - - void pointCartToPolar(double x, double y, double z, double &azimuth, double &elevation, double &distance) { - auto polar = pointCartToPolar(::Cartesian{x, y, z}); - std::tie(azimuth, elevation, distance) = std::tie(polar.az, polar.el, polar.d); - } - - CartesianPosition pointPolarToCart(const SphericalPosition &position) { - auto cart = pointPolarToCart(::Polar{position.get().get(), - position.get().get(), - position.get().get()}); - return CartesianPosition{X{static_cast(cart.x)}, - Y{static_cast(cart.y)}, - Z{static_cast(cart.z)}}; - } - - - SphericalPosition pointCartToPolar(const CartesianPosition &position) { - auto polar = pointCartToPolar(::Cartesian{position.get().get(), - position.get().get(), - position.get().get()}); - return adm::SphericalPosition(adm::Azimuth{static_cast(polar.az)}, - adm::Elevation{static_cast(polar.el)}, - adm::Distance{static_cast(polar.d)}); - } - -SphericalSpeakerPosition pointCartToPolar(const CartesianSpeakerPosition &position) { - auto polarPos = pointCartToPolar(adm::CartesianPosition{adm::X{getValueOrZero(position)}, - adm::Y{getValueOrZero(position)}, - adm::Z{getValueOrZero(position)}}); - return adm::SphericalSpeakerPosition( - polarPos.get(), - polarPos.get(), - polarPos.get()); -} - -CartesianSpeakerPosition pointPolarToCart(const SphericalSpeakerPosition &position) { - - auto cartPos = pointPolarToCart(SphericalPosition{adm::Azimuth{getValueOrZero(position)}, - adm::Elevation{getValueOrZero(position)}, - adm::Distance{getValueOrZero(position)}}); - return adm::CartesianSpeakerPosition( - cartPos.get(), - cartPos.get(), - cartPos.get()); -} - - -adm::Extent toAdmFromCartExtent(CartesianExtent extent) { - return std::make_tuple(Width{static_cast(extent.x)}, - Height{static_cast(extent.z)}, - Depth{static_cast(extent.y)}); - } - - std::tuple polarToCart(std::tuple polar) { - auto const &spherical = std::get<0>(polar); - auto cart = pointPolarToCart(spherical); - auto polarPos = toPolarFromAdm(spherical); - auto const &admPolarExtent = std::get<1>(polar); - auto extent = toAdmFromCartExtent(polarToCartExtent(polarPos, extentFromTuple(admPolarExtent))); - return {cart, extent}; - } - - std::tuple cartToPolar(std::tuple cartPosAndExtent) { - auto const &cart = std::get<0>(cartPosAndExtent); - auto const &extent = std::get<1>(cartPosAndExtent); - CartesianExtent cartExtent{std::get<0>(extent).get(), std::get<2>(extent).get(), std::get<1>(extent).get()}; - auto z = cart.has() ? cart.get() : adm::Z{}; - auto polarPos = pointCartToPolar( - ::Cartesian{cart.get().get(), cart.get().get(), z.get()}); - auto polarExtent = cartToPolarExtent(polarPos, cartExtent); - return std::make_tuple(toAdmFromPolar(polarPos), toAdmFromPolar(polarExtent)); - } -} diff --git a/reaper-adm-extension/src/reaper_adm/coordinate_conversion/coord_conv.hpp b/reaper-adm-extension/src/reaper_adm/coordinate_conversion/coord_conv.hpp deleted file mode 100644 index 5a6ad3f96..000000000 --- a/reaper-adm-extension/src/reaper_adm/coordinate_conversion/coord_conv.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -namespace adm { - using Extent = std::tuple; - std::tuple polarToCart(std::tuple polar); - std::tuple cartToPolar(std::tuple cart); - - adm::CartesianPosition pointPolarToCart(adm::SphericalPosition const& position); - adm::SphericalPosition pointCartToPolar(adm::CartesianPosition const& position); - - adm::CartesianSpeakerPosition pointPolarToCart(adm::SphericalSpeakerPosition const& speakerPosition); - adm::SphericalSpeakerPosition pointCartToPolar(adm::CartesianSpeakerPosition const& speakerPosition); - - // High precision versions - void pointPolarToCart(double azimuth, double elevation, double distance, double& x, double& y, double& z); - void pointCartToPolar(double x, double y, double z, double& azimuth, double& elevation, double& distance); - -} diff --git a/reaper-adm-extension/test/reaper_adm/CMakeLists.txt b/reaper-adm-extension/test/reaper_adm/CMakeLists.txt index 5468e777a..25550ad79 100644 --- a/reaper-adm-extension/test/reaper_adm/CMakeLists.txt +++ b/reaper-adm-extension/test/reaper_adm/CMakeLists.txt @@ -21,7 +21,6 @@ target_sources(testreaper_adm maptests.cpp tempdir.cpp valueassignertests.cpp - coordinateconversiontests.cpp automationpointtests.cpp) diff --git a/reaper-adm-extension/test/reaper_adm/coordinateconversiontests.cpp b/reaper-adm-extension/test/reaper_adm/coordinateconversiontests.cpp deleted file mode 100644 index c26e32176..000000000 --- a/reaper-adm-extension/test/reaper_adm/coordinateconversiontests.cpp +++ /dev/null @@ -1,276 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -using Catch::Approx; - -namespace { - adm::CartesianPosition makeCart(double x, double y, double z) { - return adm::CartesianPosition{adm::X{static_cast(x)}, - adm::Y{static_cast(y)}, - adm::Z{static_cast(z)}}; - } - - adm::SphericalPosition makePolar(double az, double el, double d) { - return adm::SphericalPosition{adm::Azimuth{static_cast(az)}, - adm::Elevation{static_cast(el)}, - adm::Distance{static_cast(d)}}; - } - - std::tuple elZ(float el, float z) { - return std::make_tuple(adm::Elevation(el), adm::Z(z)); - } - - std::tuple azXY(float az, float x, float y) { - return std::make_tuple(adm::Azimuth{az}, adm::X{x}, adm::Y{y}); - } - - std::vector> getRandomCoordinates(double min, double max, int count) { - std::vector> coordinates; - coordinates.reserve(count); - std::random_device r; - auto seed = r(); - INFO("Seed: " + std::to_string(seed)); - std::default_random_engine engine{seed}; - std::uniform_real_distribution dist{min, max}; - for(auto i = 0; i != count; ++i) { - coordinates.push_back({dist(engine), dist(engine), dist(engine)}); - } - return coordinates; - } - - void checkThatAllClose(adm::Extent const& input, adm::Extent const& output, adm::Extent const& expected, double eps = 1e-10) { - adm::Width inW, outW, expectW; - adm::Height inH, outH, expectH; - adm::Depth inD, outD, expectD; - std::tie(inW, inH, inD) = input; - std::tie(outW, outH, outD) = output; - std::tie(expectW, expectH, expectD) = expected; - std::stringstream ss; - ss << "input : (" << inW << ", " << inH << ", " << inD << ")\n"; - ss << "output : (" << outW << ", " << outH << ", " << outD << ")\n"; - ss << "expected : (" << expectW << ", " << expectH << ", " << expectD << ")\n"; - INFO(ss.str()); - using Catch::Matchers::WithinAbs; - CHECK(outW.get() == Approx(expectW.get()).margin(eps)); - CHECK(outH.get() == Approx(expectH.get()).margin(eps)); - CHECK(outD.get() == Approx(expectD.get()).margin(eps)); - } - - void checkThatAllClose(adm::SphericalPosition const& input, adm::CartesianPosition const& output, adm::CartesianPosition const& expected, double eps = 1e-10) { - adm::X outX, expectX; - adm::Y outY, expectY; - adm::Z outZ, expectZ; - adm::Azimuth inAz; - adm::Elevation inEl; - adm::Distance inD; - std::tie(inAz, inEl, inD) = std::make_tuple(input.get(), input.get(), input.get()); - std::tie(outX, outY, outZ) = std::make_tuple(output.get(), output.get(), output.get()); - std::tie(expectX, expectY, expectZ) = std::make_tuple(expected.get(), expected.get(), expected.get()); - - std::stringstream ss; - ss << "input (polar) : (" << inAz << ", " << inEl << ", " << inD << ")\n"; - ss << "output (cart) : (" << outX << ", " << outY << ", " << outZ << ")\n"; - ss << "expected (cart) : (" << expectX << ", " << expectY << ", " << expectZ << ")\n"; - INFO(ss.str()); - - using Catch::Matchers::WithinAbs; - CHECK(outX.get() == Approx(expectX.get()).margin(eps)); - CHECK(outY.get() == Approx(expectY.get()).margin(eps)); - CHECK(outZ.get() == Approx(expectZ.get()).margin(eps)); - } - - void checkThatAllClose(adm::CartesianPosition const& input, adm::SphericalPosition const& output, adm::SphericalPosition const& expected, double eps = 1e-10) { - adm::Azimuth outAz, expectAz; - adm::Elevation outEl, expectEl; - adm::Distance outD, expectD; - adm::X inX; - adm::Y inY; - adm::Z inZ; - std::tie(outAz, outEl, outD) = std::make_tuple(output.get(), - output.get(), - output.get()); - std::tie(inX, inY, inZ) = std::make_tuple(input.get(), - input.get(), - input.get()); - std::tie(expectAz, expectEl, expectD) = std::make_tuple(expected.get(), - expected.get(), - expected.get()); - - std::stringstream ss; - ss << "input (cartPos) (cartExtent) : (" << inX << ", " << inY << ", " << inZ << ")\n"; - ss << "output (polarPos) (polarExtent) : (" << outEl << ", " << outAz << ", " << outD << ")\n"; - ss << "expected (polarPos) : (" << expectEl << ", " << expectAz << ", " << expectD << ")\n"; - INFO(ss.str()); - - using Catch::Matchers::WithinAbs; - CHECK(outAz.get() == Approx(expectAz.get()).margin(eps)); - CHECK(outEl.get() == Approx(expectEl.get()).margin(eps)); - CHECK(outD.get() == Approx(expectD.get()).margin(eps)); - } -} - -TEST_CASE("Test conversion corners") { - std::array, 3> el_z { - elZ(-30, -1), - elZ(0, 0), - elZ(30,1) - }; - - std::array, 5> az_x_y{ - azXY(0, 0, 1), - azXY(-30, 1, 1), - azXY(30, -1, 1), - azXY(-110, 1, -1), - azXY(110, -1, -1) - }; - - std::array distances{ - adm::Distance{0.5f}, - adm::Distance{1}, - adm::Distance{2} - }; - - { - adm::Elevation el; - adm::Z z; - for(auto const& el_z_pair : el_z) { - std::tie(el, z) = el_z_pair; - adm::Azimuth az; - adm::X x; - adm::Y y; - for(auto const& az_x_y_tuple : az_x_y) { - std::tie(az, x, y) = az_x_y_tuple; - for(auto const& d : distances) { - if(el == Approx(0) || az != Approx(0)) { - auto polar = adm::SphericalPosition(az, el, d); - auto convertedCart = pointPolarToCart(polar); - auto expectedCart = adm::CartesianPosition{adm::X{x.get() * d.get()}, - adm::Y{y.get() * d.get()}, - adm::Z{z.get() * d.get()}}; - checkThatAllClose(polar, - pointPolarToCart(polar), - expectedCart); - auto convertedPolar = pointCartToPolar(expectedCart); - checkThatAllClose(expectedCart, - convertedPolar, - polar); - } - } - } - } - } -} -TEST_CASE("Test conversion poles") { - using namespace adm; - std::array minusPlus{-1.0, 1.0}; - std::array distances{0.5, 1.0, 2.0}; - for(auto sign : minusPlus) { - for(auto d : distances) { - auto polar = adm::SphericalPosition(Azimuth{0}, Elevation{sign * 90.0f}, Distance{d}); - auto convertedCart = pointPolarToCart(polar); - auto expectedCart = adm::CartesianPosition(X{0}, Y{0}, Z{sign * d}); - checkThatAllClose(polar, - convertedCart, - expectedCart); - auto convertedPolar = pointCartToPolar(expectedCart); - checkThatAllClose(expectedCart, - convertedPolar, - polar); - } - } -} - -TEST_CASE("Test conversion centre") { - using namespace adm; - std::array azimuths{-90, 0, 90}; - std::array elevations{-90, 0, 90}; - for(auto az : azimuths) { - for(auto el : elevations) { - auto polar = adm::SphericalPosition(Azimuth{az}, Elevation{el}, Distance(0.0f)); - auto convertedCart = pointPolarToCart(polar); - auto expectedCart = adm::CartesianPosition(X{0.0f}, Y{0.0f}, Z{0.0f}); - checkThatAllClose(polar, - convertedCart, - expectedCart); - } - } - auto convertedPolar = pointCartToPolar(adm::CartesianPosition(adm::X{0.f}, - adm::Y{0.f}, - adm::Z{0.f})); - auto convertedDistance = convertedPolar.get().get(); - REQUIRE(convertedDistance == Approx(0.0)); -} - - -// using the double versions of the function to check consistency with the python code. -// The intermediate conversion to float via the adm types can result in an error of the order 1e-7 which would fail test -TEST_CASE("Test conversion reversible") { - auto cartPositions = getRandomCoordinates(-2, 1, 1000); - for(auto const& pos : cartPositions) { - INFO("(x, y, z): " << "(" << pos[0] << ", " << pos[1] << ", " << pos[2] << ")"); - double az{}, el{}, d{}; - REQUIRE_NOTHROW(adm::pointCartToPolar(pos[0], pos[1], pos[2], az, el, d)); - INFO("(az, el, d): " << "(" << az << ", " << el << ", " << d << ")"); - double x{}, y{}, z{}; - REQUIRE_NOTHROW(adm::pointPolarToCart(az, el, d, x, y, z)); - checkThatAllClose(makePolar(az, el, d), makeCart(x, y, z), makeCart(pos[0], pos[1], pos[2]), 1e-10); - } -} - -namespace { - boost::optional> nextPosition(std::istream& stream) { - std::string line{}; - std::getline(stream, line); - std::stringstream lineStream{line}; - float val; - std::vector values; - while(lineStream >> val) { - values.push_back(val); - char _; - lineStream >> _; - } - if(values.size() != 12) { - return {}; - } - auto cartesianPosition = adm::CartesianPosition{adm::X{values[0]}, - adm::Y{values[1]}, - adm::Z(values[2])}; - // flipped as ear works with x,y,z extent and libadm w,h,d - // in cartesian h = z, d = y - auto cartesianExtent = adm::Extent{adm::Width{values[3]}, - adm::Height{values[5]}, - adm::Depth{values[4]}}; - auto sphericalPosition = adm::SphericalPosition{adm::Azimuth{values[6]}, - adm::Elevation{values[7]}, - adm::Distance{values[8]}}; - auto sphericalExtent = adm::Extent{adm::Width{values[9]}, - adm::Height{values[10]}, - adm::Depth{values[11]}}; - - return {std::make_tuple(cartesianPosition, cartesianExtent, sphericalPosition, sphericalExtent)}; - } -} - -TEST_CASE("Test cartesian to polar extent conversion consistent with python EAR") { - std::ifstream testData{"data/pos_extent_cart2polar.txt"}; - REQUIRE(testData.good()); - auto positions = nextPosition(testData); - adm::CartesianPosition inputPosition; - adm::Extent inputExtent; - adm::SphericalPosition expectedOutput; - adm::Extent expectedExtent; - int n{100}; - while(positions) { - std::tie(inputPosition, inputExtent, expectedOutput, expectedExtent) = *positions; - auto output = cartToPolar(std::make_tuple(inputPosition, inputExtent)); - checkThatAllClose(inputExtent, std::get<1>(output), expectedExtent, 10e-5); - --n; - positions = nextPosition(testData); - } - REQUIRE(n == 0); -} diff --git a/submodules/CMakeLists.txt b/submodules/CMakeLists.txt index 9f5cb660a..95169e6e7 100644 --- a/submodules/CMakeLists.txt +++ b/submodules/CMakeLists.txt @@ -57,12 +57,19 @@ set(BUILD_SHARED_LIBS OFF) #libadm set(BUILD_TESTING OFF) +set(BUILD_SHARED_LIBS OFF) set(UNIT_TESTS OFF CACHE BOOL "libadm unit tests" FORCE) set(ADM_HIDE_INTERNAL_SYMBOLS OFF CACHE BOOL "hide libadm symbols by default" FORCE) set(ADM_EXAMPLES OFF CACHE BOOL "Build ADM examples" FORCE) add_subdirectory(libadm) set_target_properties(adm PROPERTIES FOLDER submodules/libadm) +#AdmCoordConv +set(BUILD_TESTS OFF) +set(BUILD_SHARED_LIBS OFF) +add_subdirectory(adm_coordinate_conversion EXCLUDE_FROM_ALL) # EXCLUDE_FROM_ALL prevents AdmCoordConv default install behaviour) +set_target_properties(AdmCoordConv PROPERTIES FOLDER submodules/AdmCoordConv) + #libbw64 add_subdirectory(libbw64) add_library(IRT::bw64 INTERFACE IMPORTED GLOBAL) diff --git a/submodules/adm_coordinate_conversion b/submodules/adm_coordinate_conversion new file mode 160000 index 000000000..f44d2cae9 --- /dev/null +++ b/submodules/adm_coordinate_conversion @@ -0,0 +1 @@ +Subproject commit f44d2cae9ead752952b6259f20d9b4d42b2d2414