diff --git a/.gitignore b/.gitignore index 3a4edf69..65b3b7ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .project +CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt index b8eb0f39..08e6fb63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,12 @@ add_subdirectory (src/OpenMesh/Core) add_subdirectory (src/OpenMesh/Tools) add_subdirectory (src/OpenMesh/Apps) +set(OPENMESH_BENCHMARK_DIR CACHE PATH "Source path of benchmark (https://github.com/google/benchmark).") +if (OPENMESH_BENCHMARK_DIR) + add_subdirectory(${OPENMESH_BENCHMARK_DIR} benchmark) + add_subdirectory(src/Benchmark) +endif() + # Do not build unit tests when build as external library if(${PROJECT_NAME} MATCHES "OpenMesh") add_subdirectory (src/Unittests) diff --git a/src/Benchmark/CMakeLists.txt b/src/Benchmark/CMakeLists.txt new file mode 100644 index 00000000..59ae3c00 --- /dev/null +++ b/src/Benchmark/CMakeLists.txt @@ -0,0 +1,12 @@ +set (SOURCES "") +list (APPEND SOURCES + main.cpp + VectorT_new.cpp + VectorT_legacy.cpp + VectorT_dummy_data.cpp +) + +add_executable(OMBenchmark ${SOURCES}) +set_target_properties(OMBenchmark PROPERTIES + INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/..") +target_link_libraries(OMBenchmark benchmark OpenMeshCore) \ No newline at end of file diff --git a/src/Benchmark/VectorT.cpp b/src/Benchmark/VectorT.cpp new file mode 100644 index 00000000..54989d54 --- /dev/null +++ b/src/Benchmark/VectorT.cpp @@ -0,0 +1,163 @@ +/* + * VectorT.cpp + * + * Created on: Nov 19, 2015 + * Author: ebke + */ + +#ifndef BMPOSTFIX +#error "Do not compile directly." +#endif + +#include + +#define ASSEMBLE_(POSTFIX, PREFIX) PREFIX ## POSTFIX +#define ASSEMBLE(POSTFIX, PREFIX) ASSEMBLE_(POSTFIX, PREFIX) +#define MYBENCHMARK(NAME) BENCHMARK(NAME) +#define MYBENCHMARK_TEMPLATE(NAME, TYPE) BENCHMARK_TEMPLATE(NAME, TYPE) + +template +static inline +typename std::enable_if::type +testVec() { + return Vec(1.1, 1.2, 1.3); +} + +template +static inline +typename std::enable_if::type +testVec() { + return Vec(1.1, 1.2, 1.3, 1.4); +} + +template +static void ASSEMBLE(BMPOSTFIX, Vec_add_compare)(benchmark::State& state) { + Vec v1(0.0); + Vec v2(1000.0); + while (state.KeepRunning()) { + v1 += testVec(); + v2 -= testVec(); + if (v1 == v2) { + v1 -= v2; + v2 += v1; + } + } + // Just so nothing gets optimized away. + static double dummy; + dummy = v1.norm() + v2.norm(); + static_cast(dummy); +} + +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_add_compare), OpenMesh::Vec3d); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_add_compare), OpenMesh::Vec3f); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_add_compare), OpenMesh::Vec4d); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_add_compare), OpenMesh::Vec4f); + +template +static void ASSEMBLE(BMPOSTFIX, Vec_cross_product)(benchmark::State& state) { + Vec v1(0.0); + Vec v2(1000.0); + while (state.KeepRunning()) { + v1 += testVec(); + v2 -= testVec(); + v1 = (v1 % v2); + } + // Just so nothing gets optimized away. + static double dummy; + dummy = v1.norm() + v2.norm(); + static_cast(dummy); +} + +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_cross_product), OpenMesh::Vec3d); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_cross_product), OpenMesh::Vec3f); + +template +static void ASSEMBLE(BMPOSTFIX, Vec_scalar_product)(benchmark::State& state) { + Vec v1(0.0); + Vec v2(1000.0); + double acc = 0; + while (state.KeepRunning()) { + v1 += testVec(); + v2 -= testVec(); + acc += (v1 | v2); + } + // Otherwise GCC will optimize everything away. + static double dummy; + dummy = acc; + static_cast(dummy); +} + +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_scalar_product), OpenMesh::Vec3d); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_scalar_product), OpenMesh::Vec3f); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_scalar_product), OpenMesh::Vec4d); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_scalar_product), OpenMesh::Vec4f); + +template +static void ASSEMBLE(BMPOSTFIX, Vec_norm)(benchmark::State& state) { + Vec v1(0.0); + double acc = 0; + while (state.KeepRunning()) { + v1 += testVec(); + acc += v1.norm(); + } + // Otherwise GCC will optimize everything away. + static double dummy; + dummy = acc; + static_cast(dummy); +} + +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_norm), OpenMesh::Vec3d); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_norm), OpenMesh::Vec3f); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_norm), OpenMesh::Vec4d); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_norm), OpenMesh::Vec4f); + +template +static void ASSEMBLE(BMPOSTFIX, Vec_times_scalar)(benchmark::State& state) { + Vec v1(0.0); + while (state.KeepRunning()) { + v1 += testVec(); + v1 *= static_cast(1.0)/v1[0]; + v1 *= v1[1]; + } + // Otherwise GCC will optimize everything away. + static double dummy; + dummy = v1.norm(); + static_cast(dummy); +} + +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_times_scalar), OpenMesh::Vec3d); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_times_scalar), OpenMesh::Vec3f); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_times_scalar), OpenMesh::Vec4d); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_times_scalar), OpenMesh::Vec4f); + +#include "VectorT_dummy_data.hpp" + +template +static void ASSEMBLE(BMPOSTFIX, Vec_add_in_place)(benchmark::State& state) { + auto v = make_dummy_vector(); + while (state.KeepRunning()) { + v += v; + } + // Otherwise GCC will optimize everything away. + static double dummy = observe_dummy_vector(v); + static_cast(dummy); +} + +template +static void ASSEMBLE(BMPOSTFIX, Vec_add_temporary)(benchmark::State& state) { + auto v = make_dummy_vector(); + while (state.KeepRunning()) { + v = v + v; + } + // Otherwise GCC will optimize everything away. + static double dummy = observe_dummy_vector(v); + static_cast(dummy); +} + +typedef OpenMesh::VectorT, 3> Vec3VAd; +typedef OpenMesh::VectorT, 3> Vec3Cd; + +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_add_in_place), Vec3VAd); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_add_temporary), Vec3VAd); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_add_in_place), Vec3Cd); +MYBENCHMARK_TEMPLATE (ASSEMBLE(BMPOSTFIX, Vec_add_temporary), Vec3Cd); diff --git a/src/Benchmark/VectorT_dummy_data.cpp b/src/Benchmark/VectorT_dummy_data.cpp new file mode 100644 index 00000000..cb2ca113 --- /dev/null +++ b/src/Benchmark/VectorT_dummy_data.cpp @@ -0,0 +1,5 @@ +#include "VectorT_dummy_data.hpp" + +template<> std::valarray make_dummy_component() { + return std::valarray(128); +} diff --git a/src/Benchmark/VectorT_dummy_data.hpp b/src/Benchmark/VectorT_dummy_data.hpp new file mode 100644 index 00000000..62211c9d --- /dev/null +++ b/src/Benchmark/VectorT_dummy_data.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +template +T make_dummy_component() { + return T(); +} + +template<> std::valarray make_dummy_component(); + +template +Vec make_dummy_vector() { + return Vec(make_dummy_component()); +} + +template +double observe_dummy_component(const Scalar& _s) { + return _s; +} + +template +double observe_dummy_component(const std::valarray& _s) { + return _s.sum(); +} + +template +double observe_dummy_component(const std::complex& _s) { + return _s.real() + _s.imag(); +} + +template +double observe_dummy_vector(const Vec& _vec) { + double result = 0.0; + for (int dim = 0; dim < _vec.dim(); ++dim) { + result += observe_dummy_component(_vec[dim]); + } + return result; +} diff --git a/src/Benchmark/VectorT_legacy.cpp b/src/Benchmark/VectorT_legacy.cpp new file mode 100644 index 00000000..8063fa44 --- /dev/null +++ b/src/Benchmark/VectorT_legacy.cpp @@ -0,0 +1,14 @@ +/* + * VectorT_legacy.cpp + * + * Created on: Nov 19, 2015 + * Author: ebke + */ + +#include + +#define OPENMESH_VECTOR_LEGACY +#include + +#define BMPOSTFIX _Legacy +#include "VectorT.cpp" diff --git a/src/Benchmark/VectorT_new.cpp b/src/Benchmark/VectorT_new.cpp new file mode 100644 index 00000000..ff3e7093 --- /dev/null +++ b/src/Benchmark/VectorT_new.cpp @@ -0,0 +1,12 @@ +/* + * VectorT_new.cpp + * + * Created on: Nov 19, 2015 + * Author: ebke + */ + +#include +#include + +#define BMPOSTFIX _CPP11 +#include "VectorT.cpp" diff --git a/src/Benchmark/main.cpp b/src/Benchmark/main.cpp new file mode 100644 index 00000000..eb0427bb --- /dev/null +++ b/src/Benchmark/main.cpp @@ -0,0 +1,10 @@ +/* + * main.cpp + * + * Created on: Nov 19, 2015 + * Author: ebke + */ + +#include + +BENCHMARK_MAIN(); diff --git a/src/OpenMesh/Core/Geometry/Vector11T.hh b/src/OpenMesh/Core/Geometry/Vector11T.hh new file mode 100644 index 00000000..eefac1fb --- /dev/null +++ b/src/OpenMesh/Core/Geometry/Vector11T.hh @@ -0,0 +1,832 @@ +/* ========================================================================= * + * * + * OpenMesh * + * Copyright (c) 2001-2015, RWTH-Aachen University * + * Department of Computer Graphics and Multimedia * + * All rights reserved. * + * www.openmesh.org * + * * + *---------------------------------------------------------------------------* + * This file is part of OpenMesh. * + *---------------------------------------------------------------------------* + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright notice, * + * this list of conditions and the following disclaimer. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the following disclaimer in the * + * documentation and/or other materials provided with the distribution. * + * * + * 3. Neither the name of the copyright holder nor the names of its * + * contributors may be used to endorse or promote products derived from * + * this software without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + * * + * ========================================================================= */ + +#ifndef OPENMESH_SRC_OPENMESH_CORE_GEOMETRY_VECTOR11T_HH_ +#define OPENMESH_SRC_OPENMESH_CORE_GEOMETRY_VECTOR11T_HH_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This header is not needed by this file but expected by others including +// this file. +#include + + +/* + * Helpers for VectorT + */ +namespace { + +template +struct are_convertible_to; + +template +struct are_convertible_to { + static constexpr bool value = std::is_convertible::value + && are_convertible_to::value; +}; + +template +struct are_convertible_to : public std::is_convertible { +}; +} + +namespace OpenMesh { + +template +class VectorT { + + static_assert(DIM >= 1, "VectorT requires positive dimensionality."); + + private: + using container = std::array; + container values_; + + public: + + //---------------------------------------------------------------- class info + + /// the type of the scalar used in this template + typedef Scalar value_type; + + /// type of this vector + typedef VectorT vector_type; + + /// returns dimension of the vector (deprecated) + static constexpr int dim() { + return DIM; + } + + /// returns dimension of the vector + static constexpr size_t size() { + return DIM; + } + + static constexpr const size_t size_ = DIM; + + //-------------------------------------------------------------- constructors + + /// default constructor creates uninitialized values. + constexpr VectorT() {} + + /** + * Creates a vector with all components set to v. + */ + explicit VectorT(const Scalar &v) { + vectorize(v); + } + + template::type, + typename = typename std::enable_if< + are_convertible_to::value>::type> + constexpr VectorT(T... vs) : values_ { {static_cast(vs)...} } { + static_assert(sizeof...(T) == DIM, + "Invalid number of components specified in constructor."); + static_assert(are_convertible_to::value, + "Not all components are convertible to Scalar."); + } + + VectorT(const VectorT &rhs) = default; + VectorT(VectorT &&rhs) = default; + VectorT &operator=(const VectorT &rhs) = default; + VectorT &operator=(VectorT &&rhs) = default; + + /** + * Only for 4-component vectors with division operator on their + * Scalar: Dehomogenization. + */ + template + auto homogenized() const -> + typename std::enable_if()/std::declval()), DIM>>::type { + static_assert(D == DIM, "D and DIM need to be identical. (Never " + "override the default template arguments.)"); + static_assert(std::is_same::value, "S and Scalar need " + "to be the same type. (Never override the default template " + "arguments.)"); + return VectorT( + values_[0]/values_[3], + values_[1]/values_[3], + values_[2]/values_[3], + 1); + } + + /// construct from a value array or any other iterator + template(), void(), + ++std::declval(), void())> + explicit VectorT(Iterator it) { + std::copy_n(it, DIM, values_.begin()); + } + + /// copy & cast constructor (explicit) + template::value>> + explicit VectorT(const VectorT& _rhs) { + operator=(_rhs); + } + + //--------------------------------------------------------------------- casts + + /// cast from vector with a different scalar type + template::value>> + vector_type& operator=(const VectorT& _rhs) { + std::transform(_rhs.data(), _rhs.data() + DIM, + data(), [](OtherScalar rhs) { + return static_cast(std::move(rhs)); + }); + return *this; + } + + /// access to Scalar array + Scalar* data() { return values_.data(); } + + /// access to const Scalar array + const Scalar *data() const { return values_.data(); } + + //----------------------------------------------------------- element access + + /// get i'th element read-write + Scalar& operator[](size_t _i) { + assert(_i < DIM); + return values_[_i]; + } + + /// get i'th element read-only + const Scalar& operator[](size_t _i) const { + assert(_i < DIM); + return values_[_i]; + } + + //---------------------------------------------------------------- comparsion + + /// component-wise comparison + bool operator==(const vector_type& _rhs) const { + return std::equal(_rhs.values_.cbegin(), _rhs.values_.cend(), values_.cbegin()); + } + + /// component-wise comparison + bool operator!=(const vector_type& _rhs) const { + return !std::equal(_rhs.values_.cbegin(), _rhs.values_.cend(), values_.cbegin()); + } + + //---------------------------------------------------------- scalar operators + + /// component-wise self-multiplication with scalar + template + auto operator*=(const OtherScalar& _s) -> + typename std::enable_ifvalues_[0] * _s), Scalar>::value, + VectorT&>::type { + for (auto& e : *this) { + e *= _s; + } + return *this; + } + + /// component-wise self-division by scalar + template + auto operator/=(const OtherScalar& _s) -> + typename std::enable_ifvalues_[0] / _s), Scalar>::value, + VectorT&>::type { + for (auto& e : *this) { + e /= _s; + } + return *this; + } + + /// component-wise multiplication with scalar + template + typename std::enable_if() * std::declval()), + Scalar>::value, + VectorT>::type + operator*(const OtherScalar& _s) const { + return vector_type(*this) *= _s; + } + + /// component-wise division by with scalar + template + typename std::enable_if() / std::declval()), + Scalar>::value, + VectorT>::type + operator/(const OtherScalar& _s) const { + return vector_type(*this) /= _s; + } + + //---------------------------------------------------------- vector operators + + /// component-wise self-multiplication + template + auto operator*=(const VectorT& _rhs) -> + typename std::enable_if< + sizeof(decltype(this->values_[0] * *_rhs.data())) >= 0, + vector_type&>::type { + for (int i = 0; i < DIM; ++i) { + data()[i] *= _rhs.data()[i]; + } + return *this; + } + + /// component-wise self-division + template + auto operator/=(const VectorT& _rhs) -> + typename std::enable_if< + sizeof(decltype(this->values_[0] / *_rhs.data())) >= 0, + vector_type&>::type { + for (int i = 0; i < DIM; ++i) { + data()[i] /= _rhs.data()[i]; + } + return *this; + } + + /// vector difference from this + template + auto operator-=(const VectorT& _rhs) -> + typename std::enable_if< + sizeof(decltype(this->values_[0] - *_rhs.data())) >= 0, + vector_type&>::type { + for (int i = 0; i < DIM; ++i) { + data()[i] -= _rhs.data()[i]; + } + return *this; + } + + /// vector self-addition + template + auto operator+=(const VectorT& _rhs) -> + typename std::enable_if< + sizeof(decltype(this->values_[0] + *_rhs.data())) >= 0, + vector_type&>::type { + for (int i = 0; i < DIM; ++i) { + data()[i] += _rhs.data()[i]; + } + return *this; + } + + /// component-wise vector multiplication + template + auto operator*(const VectorT& _rhs) const -> + typename std::enable_if< + sizeof(decltype(this->values_[0] * *_rhs.data())) >= 0, + vector_type>::type { + return vector_type(*this) *= _rhs; + } + + /// component-wise vector division + template + auto operator/(const VectorT& _rhs) const -> + typename std::enable_if< + sizeof(decltype(this->values_[0] / *_rhs.data())) >= 0, + vector_type>::type { + return vector_type(*this) /= _rhs; + } + + /// component-wise vector addition + template + auto operator+(const VectorT& _rhs) const -> + typename std::enable_if< + sizeof(decltype(this->values_[0] + *_rhs.data())) >= 0, + vector_type>::type { + return vector_type(*this) += _rhs; + } + + /// component-wise vector difference + template + auto operator-(const VectorT& _rhs) const -> + typename std::enable_if< + sizeof(decltype(this->values_[0] - *_rhs.data())) >= 0, + vector_type>::type { + return vector_type(*this) -= _rhs; + } + + /// unary minus + vector_type operator-(void) const { + vector_type v; + std::transform(values_.begin(), values_.end(), v.values_.begin(), + [](const Scalar &s) { return -s; }); + return v; + } + + /// cross product: only defined for Vec3* as specialization + /// \see OpenMesh::cross + template + auto operator% (const VectorT &_rhs) const -> + typename std::enable_ifvalues_[0] * _rhs[0] - + this->values_[0] * _rhs[0]), + DIM>>::type { + return { + values_[1] * _rhs[2] - values_[2] * _rhs[1], + values_[2] * _rhs[0] - values_[0] * _rhs[2], + values_[0] * _rhs[1] - values_[1] * _rhs[0] + }; + } + + /// compute scalar product + /// \see OpenMesh::dot + template + auto operator|(const VectorT& _rhs) const -> + decltype(*this->data() * *_rhs.data()) { + + return std::inner_product(data() + 1, data() + DIM, _rhs.data() + 1, + *data() * *_rhs.data()); + } + + //------------------------------------------------------------ euclidean norm + + /// \name Euclidean norm calculations + //@{ + + /// compute squared euclidean norm + template + decltype(std::declval() * std::declval()) sqrnorm() const { + static_assert(std::is_same::value, "S and Scalar need " + "to be the same type. (Never override the default template " + "arguments.)"); + typedef decltype(values_[0] * values_[0]) RESULT; + return std::accumulate(values_.cbegin() + 1, values_.cend(), + values_[0] * values_[0], + [](const RESULT &l, const Scalar &r) { return l + r * r; }); + } + + /// compute euclidean norm + template + auto norm() const -> + decltype(std::sqrt(std::declval>().sqrnorm())) { + static_assert(std::is_same::value, "S and Scalar need " + "to be the same type. (Never override the default template " + "arguments.)"); + return std::sqrt(sqrnorm()); + } + + template + auto length() const -> + decltype(std::declval>().norm()) { + static_assert(std::is_same::value, "S and Scalar need " + "to be the same type. (Never override the default template " + "arguments.)"); + return norm(); + } + + /** normalize vector, return normalized vector + */ + template + auto normalize() -> + decltype(*this /= std::declval>().norm()) { + static_assert(std::is_same::value, "S and Scalar need " + "to be the same type. (Never override the default template " + "arguments.)"); + return *this /= norm(); + } + + /** return normalized vector + */ + template + auto normalized() const -> + decltype(*this / std::declval>().norm()) { + static_assert(std::is_same::value, "S and Scalar need " + "to be the same type. (Never override the default template " + "arguments.)"); + return *this / norm(); + } + + /** normalize vector, return normalized vector and avoids div by zero + */ + template + typename std::enable_if< + sizeof(decltype( + static_cast(0), + std::declval>().norm())) >= 0, + vector_type&>::type + normalize_cond() { + static_assert(std::is_same::value, "S and Scalar need " + "to be the same type. (Never override the default template " + "arguments.)"); + auto n = norm(); + if (n != static_cast(0)) { + *this /= n; + } + return *this; + } + + //@} + + //------------------------------------------------------------ euclidean norm + + /// \name Non-Euclidean norm calculations + //@{ + + /// compute L1 (Manhattan) norm + Scalar l1_norm() const { + return std::accumulate( + values_.cbegin() + 1, values_.end(), values_[0]); + } + + /// compute l8_norm + Scalar l8_norm() const { + return max_abs(); + } + + //@} + + //------------------------------------------------------------ max, min, mean + + /// \name Minimum maximum and mean + //@{ + + /// return the maximal component + Scalar max() const { + return *std::max_element(values_.cbegin(), values_.cend()); + } + + /// return the maximal absolute component + Scalar max_abs() const { + return std::abs( + *std::max_element(values_.cbegin(), values_.cend(), + [](const Scalar &a, const Scalar &b) { + return std::abs(a) < std::abs(b); + })); + } + + /// return the minimal component + Scalar min() const { + return *std::min_element(values_.cbegin(), values_.cend()); + } + + /// return the minimal absolute component + Scalar min_abs() const { + return std::abs( + *std::min_element(values_.cbegin(), values_.cend(), + [](const Scalar &a, const Scalar &b) { + return std::abs(a) < std::abs(b); + })); + } + + /// return arithmetic mean + Scalar mean() const { + return l1_norm()/DIM; + } + + /// return absolute arithmetic mean + Scalar mean_abs() const { + return std::accumulate(values_.cbegin() + 1, values_.end(), + std::abs(values_[0]), + [](const Scalar &l, const Scalar &r) { + return l + std::abs(r); + }) / DIM; + } + + /// minimize values: same as *this = min(*this, _rhs), but faster + vector_type& minimize(const vector_type& _rhs) { + std::transform(values_.cbegin(), values_.cend(), + _rhs.values_.cbegin(), + values_.begin(), + [](const Scalar &l, const Scalar &r) { + return std::min(l, r); + }); + return *this; + } + + /// minimize values and signalize coordinate minimization + bool minimized(const vector_type& _rhs) { + bool result = false; + std::transform(values_.cbegin(), values_.cend(), + _rhs.values_.cbegin(), + values_.begin(), + [&result](const Scalar &l, const Scalar &r) { + if (l < r) { + return l; + } else { + result = true; + return r; + } + }); + return result; + } + + /// maximize values: same as *this = max(*this, _rhs), but faster + vector_type& maximize(const vector_type& _rhs) { + std::transform(values_.cbegin(), values_.cend(), + _rhs.values_.cbegin(), + values_.begin(), + [](const Scalar &l, const Scalar &r) { + return std::max(l, r); + }); + return *this; + } + + /// maximize values and signalize coordinate maximization + bool maximized(const vector_type& _rhs) { + bool result = false; + std::transform(values_.cbegin(), values_.cend(), + _rhs.values_.cbegin(), + values_.begin(), + [&result](const Scalar &l, const Scalar &r) { + if (l > r) { + return l; + } else { + result = true; + return r; + } + }); + return result; + } + + /// component-wise min + inline vector_type min(const vector_type& _rhs) const { + return vector_type(*this).minimize(_rhs); + } + + /// component-wise max + inline vector_type max(const vector_type& _rhs) const { + return vector_type(*this).maximize(_rhs); + } + + //@} + + //------------------------------------------------------------ misc functions + + /// component-wise apply function object with Scalar operator()(Scalar). + template + inline vector_type apply(const Functor& _func) const { + vector_type result; + std::transform(result.values_.begin(), result.values_.end(), + result.values_.begin(), _func); + return result; + } + + /// store the same value in each component (e.g. to clear all entries) + vector_type& vectorize(const Scalar& _s) { + std::fill(values_.begin(), values_.end(), _s); + return *this; + } + + /// store the same value in each component + static vector_type vectorized(const Scalar& _s) { + return vector_type().vectorize(_s); + } + + /// lexicographical comparison + bool operator<(const vector_type& _rhs) const { + return std::lexicographical_compare( + values_.begin(), values_.end(), + _rhs.values_.begin(), _rhs.values_.end()); + } + + //------------------------------------------------------------ component iterators + + /// \name Component iterators + //@{ + + using iterator = typename container::iterator; + using const_iterator = typename container::const_iterator; + using reverse_iterator = typename container::reverse_iterator; + using const_reverse_iterator = typename container::const_reverse_iterator; + + iterator begin() noexcept { return values_.begin(); } + const_iterator begin() const noexcept { return values_.cbegin(); } + const_iterator cbegin() const noexcept { return values_.cbegin(); } + + iterator end() noexcept { return values_.end(); } + const_iterator end() const noexcept { return values_.cend(); } + const_iterator cend() const noexcept { return values_.cend(); } + + reverse_iterator rbegin() noexcept { return values_.rbegin(); } + const_reverse_iterator rbegin() const noexcept { return values_.crbegin(); } + const_reverse_iterator crbegin() const noexcept { return values_.crbegin(); } + + reverse_iterator rend() noexcept { return values_.rend(); } + const_reverse_iterator rend() const noexcept { return values_.crend(); } + const_reverse_iterator crend() const noexcept { return values_.crend(); } + + //@} +}; + +/// Component wise multiplication from the left +template +auto operator*(const OtherScalar& _s, const VectorT &rhs) -> + decltype(rhs.operator*(_s)) { + + return rhs * _s; +} + +/// output a vector by printing its space-separated compontens +template +auto operator<<(std::ostream& os, const VectorT &_vec) -> + typename std::enable_if< + sizeof(decltype(os << _vec[0])) >= 0, std::ostream&>::type { + + os << _vec[0]; + for (int i = 1; i < DIM; ++i) { + os << " " << _vec[i]; + } + return os; +} + +/// read the space-separated components of a vector from a stream +template +auto operator>> (std::istream& is, VectorT &_vec) -> + typename std::enable_if< + sizeof(decltype(is >> _vec[0])) >= 0, std::istream &>::type { + for (int i = 0; i < DIM; ++i) + is >> _vec[i]; + return is; +} + +/// \relates OpenMesh::VectorT +/// symmetric version of the dot product +template +Scalar dot(const VectorT& _v1, const VectorT& _v2) { + return (_v1 | _v2); +} + + +/// \relates OpenMesh::VectorT +/// symmetric version of the cross product +template +auto +cross(const VectorT& _v1, const VectorT& _v2) -> + decltype(_v1 % _v2) { + return (_v1 % _v2); +} + + +//== TYPEDEFS ================================================================= + +/** 1-byte signed vector */ +typedef VectorT Vec1c; +/** 1-byte unsigned vector */ +typedef VectorT Vec1uc; +/** 1-short signed vector */ +typedef VectorT Vec1s; +/** 1-short unsigned vector */ +typedef VectorT Vec1us; +/** 1-int signed vector */ +typedef VectorT Vec1i; +/** 1-int unsigned vector */ +typedef VectorT Vec1ui; +/** 1-float vector */ +typedef VectorT Vec1f; +/** 1-double vector */ +typedef VectorT Vec1d; + +/** 2-byte signed vector */ +typedef VectorT Vec2c; +/** 2-byte unsigned vector */ +typedef VectorT Vec2uc; +/** 2-short signed vector */ +typedef VectorT Vec2s; +/** 2-short unsigned vector */ +typedef VectorT Vec2us; +/** 2-int signed vector */ +typedef VectorT Vec2i; +/** 2-int unsigned vector */ +typedef VectorT Vec2ui; +/** 2-float vector */ +typedef VectorT Vec2f; +/** 2-double vector */ +typedef VectorT Vec2d; + +/** 3-byte signed vector */ +typedef VectorT Vec3c; +/** 3-byte unsigned vector */ +typedef VectorT Vec3uc; +/** 3-short signed vector */ +typedef VectorT Vec3s; +/** 3-short unsigned vector */ +typedef VectorT Vec3us; +/** 3-int signed vector */ +typedef VectorT Vec3i; +/** 3-int unsigned vector */ +typedef VectorT Vec3ui; +/** 3-float vector */ +typedef VectorT Vec3f; +/** 3-double vector */ +typedef VectorT Vec3d; +/** 3-bool vector */ +typedef VectorT Vec3b; + +/** 4-byte signed vector */ +typedef VectorT Vec4c; +/** 4-byte unsigned vector */ +typedef VectorT Vec4uc; +/** 4-short signed vector */ +typedef VectorT Vec4s; +/** 4-short unsigned vector */ +typedef VectorT Vec4us; +/** 4-int signed vector */ +typedef VectorT Vec4i; +/** 4-int unsigned vector */ +typedef VectorT Vec4ui; +/** 4-float vector */ +typedef VectorT Vec4f; +/** 4-double vector */ +typedef VectorT Vec4d; + +/** 5-byte signed vector */ +typedef VectorT Vec5c; +/** 5-byte unsigned vector */ +typedef VectorT Vec5uc; +/** 5-short signed vector */ +typedef VectorT Vec5s; +/** 5-short unsigned vector */ +typedef VectorT Vec5us; +/** 5-int signed vector */ +typedef VectorT Vec5i; +/** 5-int unsigned vector */ +typedef VectorT Vec5ui; +/** 5-float vector */ +typedef VectorT Vec5f; +/** 5-double vector */ +typedef VectorT Vec5d; + +/** 6-byte signed vector */ +typedef VectorT Vec6c; +/** 6-byte unsigned vector */ +typedef VectorT Vec6uc; +/** 6-short signed vector */ +typedef VectorT Vec6s; +/** 6-short unsigned vector */ +typedef VectorT Vec6us; +/** 6-int signed vector */ +typedef VectorT Vec6i; +/** 6-int unsigned vector */ +typedef VectorT Vec6ui; +/** 6-float vector */ +typedef VectorT Vec6f; +/** 6-double vector */ +typedef VectorT Vec6d; + +} // namespace OpenMesh + +/** + * Literal operator for inline specification of colors in HTML syntax. + * + * Example: + * \code{.cpp} + * OpenMesh::Vec4f light_blue = 0x1FCFFFFF_htmlColor; + * \endcode + */ +constexpr OpenMesh::Vec4f operator"" _htmlColor(unsigned long long raw_color) { + return OpenMesh::Vec4f( + ((raw_color >> 24) & 0xFF) / 255.0f, + ((raw_color >> 16) & 0xFF) / 255.0f, + ((raw_color >> 8) & 0xFF) / 255.0f, + ((raw_color >> 0) & 0xFF) / 255.0f); +} + +#endif /* OPENMESH_SRC_OPENMESH_CORE_GEOMETRY_VECTOR11T_HH_ */ diff --git a/src/OpenMesh/Core/Geometry/VectorT.hh b/src/OpenMesh/Core/Geometry/VectorT.hh index 433120a0..5e2aca6f 100644 --- a/src/OpenMesh/Core/Geometry/VectorT.hh +++ b/src/OpenMesh/Core/Geometry/VectorT.hh @@ -59,6 +59,10 @@ // bugreport: https://bugzilla.gnome.org/show_bug.cgi?id=629182) // macro expansion and preprocessor defines // don't work properly. + +#if (__cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__)) && !defined(OPENMESH_VECTOR_LEGACY) +#include "Vector11T.hh" +#else #ifndef DOXYGEN #ifndef OPENMESH_VECTOR_HH @@ -77,12 +81,6 @@ #include #endif -#if __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) -#include -#include -#include -#endif - //== NAMESPACES =============================================================== @@ -92,24 +90,6 @@ namespace OpenMesh { //== CLASS DEFINITION ========================================================= -#if __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) -/* - * Helpers for VectorT - */ -namespace { -template -struct are_convertible_to; - -template -struct are_convertible_to { - static constexpr bool value = std::is_convertible::value && are_convertible_to::value; -}; -template -struct are_convertible_to : public std::is_convertible {}; -} -#endif - - /** The N values of the template Scalar type are the only data members of the class VectorT. This guarantees 100% compatibility with arrays of type Scalar and size N, allowing us to define the @@ -121,18 +101,7 @@ struct are_convertible_to : public std::is_convertible {}; */ template class VectorDataT { public: -#if __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) - VectorDataT() {} - - template - constexpr VectorDataT(T... vs) : values_ {{vs...}} { - static_assert(sizeof...(vs) == N, - "Incorrect number of vector components supplied."); - } - std::array values_; -#else Scalar values_[N]; -#endif }; @@ -141,22 +110,9 @@ template class VectorDataT { /// This specialization enables us to use aligned SSE instructions. template<> class VectorDataT { public: -#if __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) - VectorDataT() {} - - template - constexpr VectorDataT(T... vs) : values_ {{vs...}} { - static_assert(sizeof...(vs) == 4, - "Incorrect number of vector components supplied."); - } -#endif union { __m128 m128; -#if __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) - std::array values_; -#else float values_[4]; -#endif }; }; @@ -432,24 +388,7 @@ typedef VectorT Vec6d; //============================================================================= -#if __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) -/** - * Literal operator for inline specification of colors in HTML syntax. - * - * Example: - * \code{.cpp} - * OpenMesh::Vec4f light_blue = 0x1FCFFFFF_htmlColor; - * \endcode - */ -constexpr OpenMesh::Vec4f operator"" _htmlColor(unsigned long long raw_color) { - return OpenMesh::Vec4f( - ((raw_color >> 24) & 0xFF) / 255.0f, - ((raw_color >> 16) & 0xFF) / 255.0f, - ((raw_color >> 8) & 0xFF) / 255.0f, - ((raw_color >> 0) & 0xFF) / 255.0f); -} -#endif - #endif // OPENMESH_VECTOR_HH defined //============================================================================= #endif // DOXYGEN +#endif // C++11 diff --git a/src/OpenMesh/Core/Geometry/VectorT_inc.hh b/src/OpenMesh/Core/Geometry/VectorT_inc.hh index 97bc055f..96b6c100 100644 --- a/src/OpenMesh/Core/Geometry/VectorT_inc.hh +++ b/src/OpenMesh/Core/Geometry/VectorT_inc.hh @@ -97,28 +97,6 @@ public: /// default constructor creates uninitialized values. inline VectorT() {} -#if __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) - explicit inline VectorT(const Scalar &v) { - vectorize(v); - } - - template::type, - typename = typename std::enable_if::value>::type> - constexpr VectorT(T... vs) : Base { static_cast(vs)...} - { } - - template::type> - typename std::enable_if::type - homogenized() const { - return VectorT( - Base::values_[0]/Base::values_[3], - Base::values_[1]/Base::values_[3], - Base::values_[2]/Base::values_[3], - 1); - } - -#else /// special constructor for 1D vectors explicit inline VectorT(const Scalar& v) { // assert(DIM==1); @@ -165,7 +143,6 @@ public: Base::values_[0]=v0; Base::values_[1]=v1; Base::values_[2]=v2; Base::values_[3]=v3; Base::values_[4]=v4; Base::values_[5]=v5; } -#endif #endif /// construct from a value array (explicit) @@ -210,21 +187,11 @@ public: // /// cast to const Scalar array // inline operator const Scalar*() const { return Base::values_; } -#if __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) - /// access to Scalar array - inline Scalar* data() { return Base::values_.data(); } - - /// access to const Scalar array - inline const Scalar*data() const { return Base::values_.data(); } -#else /// access to Scalar array inline Scalar* data() { return Base::values_; } /// access to const Scalar array inline const Scalar*data() const { return Base::values_; } -#endif - - //----------------------------------------------------------- element access diff --git a/src/Python/Vector.hh b/src/Python/Vector.hh index f43cd14b..68cfaf94 100644 --- a/src/Python/Vector.hh +++ b/src/Python/Vector.hh @@ -38,6 +38,71 @@ Scalar get_item(Vector& _vec, int _index) { return 0.0; } +namespace { +template +struct Factory { + typedef OpenMesh::VectorT Vector2; + typedef OpenMesh::VectorT Vector3; + typedef OpenMesh::VectorT Vector4; + + static Vector2 *vec2_default() { + return new Vector2(Scalar(), Scalar()); + } + static Vector2 *vec2_user_defined(const Scalar& _v0, const Scalar& _v1) { + return new Vector2(_v0, _v1); + } + static Vector3 *vec3_default() { + return new Vector3(Scalar(), Scalar(), Scalar()); + } + static Vector3 *vec3_user_defined(const Scalar& _v0, const Scalar& _v1, const Scalar& _v2) { + return new Vector3(_v0, _v1, _v2); + } + static Vector4 *vec4_default() { + return new Vector4(Scalar(), Scalar(), Scalar(), Scalar()); + } + static Vector4 *vec4_user_defined(const Scalar& _v0, const Scalar& _v1, const Scalar& _v2, const Scalar& _v3) { + return new Vector4(_v0, _v1, _v2, _v3); + } +}; +} + +template +void defInitMod(class_> &classVector); + +template +void defInitMod(class_> &classVector) { + classVector + .def("__init__", make_constructor(&Factory::vec2_default)) + .def("__init__", make_constructor(&Factory::vec2_user_defined)) + ; + + typedef OpenMesh::VectorT Vector; + def("dot", &Vector::operator|); +} +template +void defInitMod(class_> &classVector) { + classVector + .def("__init__", make_constructor(&Factory::vec3_default)) + .def("__init__", make_constructor(&Factory::vec3_user_defined)) + .def("__mod__", &Factory::Vector3::operator%) + ; + + def("cross", &Factory::Vector3::operator%); + + typedef OpenMesh::VectorT Vector; + def("dot", &Vector::operator|); +} +template +void defInitMod(class_> &classVector) { + classVector + .def("__init__", make_constructor(&Factory::vec4_default)) + .def("__init__", make_constructor(&Factory::vec4_user_defined)) + ; + + typedef OpenMesh::VectorT Vector; + def("dot", &Vector::operator|); +} + /** * Expose a vector type to %Python. * @@ -86,12 +151,12 @@ void expose_vec(const char *_name) { .def("vectorize", &Vector::vectorize, return_internal_reference<>()) .def(self < self) - .def("norm", &Vector::norm) - .def("length", &Vector::length) - .def("sqrnorm", &Vector::sqrnorm) - .def("normalize", &Vector::normalize, return_internal_reference<>()) - .def("normalized", &Vector::normalized) - .def("normalize_cond", &Vector::normalize_cond, return_internal_reference<>()) + .def("norm", &Vector::template norm) + .def("length", &Vector::template length) + .def("sqrnorm", &Vector::template sqrnorm) + .def("normalize", &Vector::template normalize, return_internal_reference<>()) + .def("normalized", &Vector::template normalized) + .def("normalize_cond", &Vector::template normalize_cond, return_internal_reference<>()) .def("l1_norm", &Vector::l1_norm) .def("l8_norm", &Vector::l8_norm) @@ -115,56 +180,7 @@ void expose_vec(const char *_name) { .staticmethod("vectorized") ; - typedef OpenMesh::VectorT Vector2; - typedef OpenMesh::VectorT Vector3; - typedef OpenMesh::VectorT Vector4; - - struct Factory { - static Vector2 *vec2_default() { - return new Vector2(Scalar(), Scalar()); - } - static Vector2 *vec2_user_defined(const Scalar& _v0, const Scalar& _v1) { - return new Vector2(_v0, _v1); - } - static Vector3 *vec3_default() { - return new Vector3(Scalar(), Scalar(), Scalar()); - } - static Vector3 *vec3_user_defined(const Scalar& _v0, const Scalar& _v1, const Scalar& _v2) { - return new Vector3(_v0, _v1, _v2); - } - static Vector4 *vec4_default() { - return new Vector4(Scalar(), Scalar(), Scalar(), Scalar()); - } - static Vector4 *vec4_user_defined(const Scalar& _v0, const Scalar& _v1, const Scalar& _v2, const Scalar& _v3) { - return new Vector4(_v0, _v1, _v2, _v3); - } - }; - - if (N == 2) { - classVector - .def("__init__", make_constructor(&Factory::vec2_default)) - .def("__init__", make_constructor(&Factory::vec2_user_defined)) - ; - } - - if (N == 3) { - classVector - .def("__init__", make_constructor(&Factory::vec3_default)) - .def("__init__", make_constructor(&Factory::vec3_user_defined)) - .def("__mod__", &Vector3::operator%) - ; - - def("cross", &Vector3::operator%); - } - - if (N == 4) { - classVector - .def("__init__", make_constructor(&Factory::vec4_default)) - .def("__init__", make_constructor(&Factory::vec4_user_defined)) - ; - } - - def("dot", &Vector::operator|); + defInitMod(classVector); } } // namespace OpenMesh diff --git a/src/Unittests/.gitignore b/src/Unittests/.gitignore new file mode 100644 index 00000000..868a01f9 --- /dev/null +++ b/src/Unittests/.gitignore @@ -0,0 +1,7 @@ +deleted_output.off +output.off +persistence-check.om +smoothed_STL_output.off +smoothed_custom_properties_output.off +smoothed_extended_output.off +smoothed_output.off diff --git a/src/Unittests/TestFiles/.gitignore b/src/Unittests/TestFiles/.gitignore new file mode 100644 index 00000000..4f6adcfd --- /dev/null +++ b/src/Unittests/TestFiles/.gitignore @@ -0,0 +1,6 @@ +cube-minimal-customprops_openmeshOutputTestfileBinary.ply +cube_floating.off +cube_floating_binary.off +meshlab_binary.ply +meshlab_binary_float.ply +meshlab_float.ply diff --git a/src/Unittests/unittests_cpp_11_features.cc b/src/Unittests/unittests_cpp_11_features.cc index ec3cb64b..72915491 100644 --- a/src/Unittests/unittests_cpp_11_features.cc +++ b/src/Unittests/unittests_cpp_11_features.cc @@ -82,6 +82,7 @@ TEST_F(OpenMesh_Triangle, cpp11_vertexrange_test) { for(Mesh::VertexHandle t : mesh_.vertices()) { FAIL() << "The Vertexrange for an empty Mesh is not empty"; + EXPECT_TRUE(t.is_valid()); // Just so we don't get an unused variable warning. } //add vertices to mesh @@ -110,6 +111,7 @@ TEST_F(OpenMesh_Poly, cpp11_vertexrange_test) { for(PolyMesh::VertexHandle t : mesh_.vertices()) { FAIL() << "The Vertexrange for an empty Mesh is not empty"; + EXPECT_TRUE(t.is_valid()); // Just so we don't get an unused variable warning. } //add vertices to mesh @@ -140,6 +142,7 @@ TEST_F(OpenMesh_Triangle, cpp11_vvrange_test) { for(Mesh::VertexHandle t : mesh_.vv_range(vhandle[0])) { FAIL() << "The vvrange for a single vertex in a TriMesh is not empty"; + EXPECT_TRUE(t.is_valid()); // Just so we don't get an unused variable warning. } //add more vertices @@ -198,6 +201,7 @@ TEST_F(OpenMesh_Poly, cpp11_vvrange_test) { for(PolyMesh::VertexHandle t : mesh_.vv_range(vhandle[0])) { FAIL() << "The vvrange for a single vertex in a PolyMesh is not empty"; + EXPECT_TRUE(t.is_valid()); // Just so we don't get an unused variable warning. } //add more vertices diff --git a/src/Unittests/unittests_trimesh_others.cc b/src/Unittests/unittests_trimesh_others.cc index 03b056e7..14d96e37 100644 --- a/src/Unittests/unittests_trimesh_others.cc +++ b/src/Unittests/unittests_trimesh_others.cc @@ -169,7 +169,7 @@ TEST_F(OpenMeshOthers, CalcDihedralAngre ) { double difference = fabs( 1.36944 - mesh_.calc_dihedral_angle(eh) ); - EXPECT_TRUE( (difference < 0.00001 ) ) << "Wrong Dihedral angle, Difference is to big!" << std::endl; + EXPECT_LT(difference, 0.00001) << "Wrong Dihedral angle, Difference is to big!" << std::endl; } } diff --git a/src/Unittests/unittests_vector_type.cc b/src/Unittests/unittests_vector_type.cc index 82e7793b..8ca929ca 100644 --- a/src/Unittests/unittests_vector_type.cc +++ b/src/Unittests/unittests_vector_type.cc @@ -1,6 +1,7 @@ #include #include #include +#include namespace { @@ -115,6 +116,8 @@ TEST_F(OpenMeshVectorTest, cpp11_constructors) { TEST_F(OpenMeshVectorTest, cpp11_htmlColorLiteral) { static constexpr OpenMesh::Vec4f rose = 0xFFC7F1FF_htmlColor; + EXPECT_EQ(0xFFC7F1FF_htmlColor, rose); + const OpenMesh::Vec4f light_blue = 0x1FCFFFFF_htmlColor; EXPECT_LE((OpenMesh::Vec4f(0.1215686274f, 0.8117647058f, 1.0f, 1.0f) - light_blue).sqrnorm(), 1e-10); @@ -125,6 +128,199 @@ TEST_F(OpenMeshVectorTest, cpp11_htmlColorLiteral) { ::value, "Bad type deduced from _htmlColor literal."); EXPECT_EQ(light_blue, light_blue_2); } -#endif + + +namespace { +class C { + public: + C() {} + C(const C &rhs) { ADD_FAILURE() << "Copy constructor used."; } + C(C &&rhs) { ++copy_con; } + C &operator= (const C &rhs) { + ADD_FAILURE() << "Copy assignemnt used."; + return *this; + } + C &operator= (C &&rhs) { ++copy_ass; return *this; } + + static int copy_con; + static int copy_ass; +}; + +int C::copy_con = 0; +int C::copy_ass = 0; +} + +/** + * Checks two things: + * 1) Whether VectorT works with a non-arithmetic type. + * 2) Whether move construction and assignment works. + */ +TEST_F(OpenMeshVectorTest, move_constructor_assignment) { + + C::copy_con = 0; + C::copy_ass = 0; + + // Test move assigning. + OpenMesh::VectorT x, y; + x = std::move(y); + EXPECT_EQ(3, C::copy_ass); + EXPECT_EQ(0, C::copy_con); + + // Test move constructing. + OpenMesh::VectorT z(std::move(x)); + EXPECT_EQ(3, C::copy_ass); + EXPECT_EQ(3, C::copy_con); +} + +TEST_F(OpenMeshVectorTest, iterator_init) { + std::list a; + a.push_back(1.0); + a.push_back(2.0); + a.push_back(3.0); + OpenMesh::Vec3f v(a.begin()); + EXPECT_EQ(OpenMesh::Vec3f(1.0, 2.0, 3.0), v); +} + +#endif // C++11 + + +TEST_F(OpenMeshVectorTest, BasicArithmeticInPlace) { + OpenMesh::Vec3d v1(1, 2, 3); + double s1 = 2; + const double epsilon = 1e-6; + + OpenMesh::Vec3d v2add (3, 4, 6); v2add += v1; + OpenMesh::Vec3d v2sub (3, 4, 6); v2sub -= v1; + OpenMesh::Vec3d v2cmul(3, 4, 6); v2cmul *= v1; + OpenMesh::Vec3d v2cdiv(3, 4, 6); v2cdiv /= v1; + OpenMesh::Vec3d v2smul(3, 4, 6); v2smul *= s1; + OpenMesh::Vec3d v2sdiv(3, 4, 6); v2sdiv /= s1; + + EXPECT_NEAR(4, v2add[0], epsilon); + EXPECT_NEAR(6, v2add[1], epsilon); + EXPECT_NEAR(9, v2add[2], epsilon); + + EXPECT_NEAR(2, v2sub[0], epsilon); + EXPECT_NEAR(2, v2sub[1], epsilon); + EXPECT_NEAR(3, v2sub[2], epsilon); + + EXPECT_NEAR( 3, v2cmul[0], epsilon); + EXPECT_NEAR( 8, v2cmul[1], epsilon); + EXPECT_NEAR(18, v2cmul[2], epsilon); + + EXPECT_NEAR(3, v2cdiv[0], epsilon); + EXPECT_NEAR(2, v2cdiv[1], epsilon); + EXPECT_NEAR(2, v2cdiv[2], epsilon); + + EXPECT_NEAR( 6, v2smul[0], epsilon); + EXPECT_NEAR( 8, v2smul[1], epsilon); + EXPECT_NEAR(12, v2smul[2], epsilon); + + EXPECT_NEAR(1.5, v2sdiv[0], epsilon); + EXPECT_NEAR(2.0, v2sdiv[1], epsilon); + EXPECT_NEAR(3.0, v2sdiv[2], epsilon); +} + +TEST_F(OpenMeshVectorTest, BasicArithmeticImmutable) { + OpenMesh::Vec3d v1(1, 2, 3); + const double epsilon = 1e-6; + + OpenMesh::Vec3d v2add = v1 + OpenMesh::Vec3d(2, 4, 6); + OpenMesh::Vec3d v2sub = v1 - OpenMesh::Vec3d(2, 4, 6); + OpenMesh::Vec3d v2cmul = v1 * OpenMesh::Vec3d(2, 4, 6); + OpenMesh::Vec3d v2cdiv = v1 / OpenMesh::Vec3d(2, 4, 6); + OpenMesh::Vec3d v2smul = v1 * 2.0; + OpenMesh::Vec3d v2sdiv = v1 / 2.0; + OpenMesh::Vec3d v2neg = -v1; + + EXPECT_NEAR(3, v2add[0], epsilon); + EXPECT_NEAR(6, v2add[1], epsilon); + EXPECT_NEAR(9, v2add[2], epsilon); + + EXPECT_NEAR(-1, v2sub[0], epsilon); + EXPECT_NEAR(-2, v2sub[1], epsilon); + EXPECT_NEAR(-3, v2sub[2], epsilon); + + EXPECT_NEAR( 2, v2cmul[0], epsilon); + EXPECT_NEAR( 8, v2cmul[1], epsilon); + EXPECT_NEAR(18, v2cmul[2], epsilon); + + EXPECT_NEAR(0.5, v2cdiv[0], epsilon); + EXPECT_NEAR(0.5, v2cdiv[1], epsilon); + EXPECT_NEAR(0.5, v2cdiv[2], epsilon); + + EXPECT_NEAR(2, v2smul[0], epsilon); + EXPECT_NEAR(4, v2smul[1], epsilon); + EXPECT_NEAR(6, v2smul[2], epsilon); + + EXPECT_NEAR(0.5, v2sdiv[0], epsilon); + EXPECT_NEAR(1.0, v2sdiv[1], epsilon); + EXPECT_NEAR(1.5, v2sdiv[2], epsilon); + + EXPECT_NEAR(-1, v2neg[0], epsilon); + EXPECT_NEAR(-2, v2neg[1], epsilon); + EXPECT_NEAR(-3, v2neg[2], epsilon); +} + +TEST_F(OpenMeshVectorTest, BasicLinearAlgebra) { + OpenMesh::Vec3d v(1, 2, 3); + EXPECT_EQ(v[0], 1.0); + EXPECT_EQ(v[1], 2.0); + EXPECT_EQ(v[2], 3.0); + + EXPECT_EQ(OpenMesh::Vec3d(-1, -2, -3), -v); + EXPECT_EQ(3, OpenMesh::Vec3d(1, 3, 2).max()); + EXPECT_EQ(3, OpenMesh::Vec3d(1, 2, 3).max()); + EXPECT_EQ(3, OpenMesh::Vec3d(1, 3, -4).max()); + EXPECT_EQ(3, OpenMesh::Vec3d(-4, 2, 3).max()); + EXPECT_EQ(4, OpenMesh::Vec3d(1, 3, -4).max_abs()); + EXPECT_EQ(4, OpenMesh::Vec3d(-4, 2, 3).max_abs()); + + EXPECT_EQ(1, OpenMesh::Vec3d(1, 3, 2).min()); + EXPECT_EQ(1, OpenMesh::Vec3d(1, 2, 3).min()); + EXPECT_EQ(-4, OpenMesh::Vec3d(1, 3, -4).min()); + EXPECT_EQ(-4, OpenMesh::Vec3d(-4, 2, 3).min()); + EXPECT_EQ(1, OpenMesh::Vec3d(1, 3, -4).min_abs()); + EXPECT_EQ(2, OpenMesh::Vec3d(-4, 2, 3).min_abs()); + + EXPECT_NEAR(14, OpenMesh::Vec3d(1, 2, 3) | OpenMesh::Vec3d(1, 2, 3), 1e-6); + EXPECT_NEAR(-14, OpenMesh::Vec3d(1, 2, 3) | OpenMesh::Vec3d(-1, -2, -3), 1e-6); + EXPECT_NEAR(14, OpenMesh::Vec3d(-1, -2, -3) | OpenMesh::Vec3d(-1, -2, -3), 1e-6); +} + +TEST_F(OpenMeshVectorTest, array_init) { + float a[3]; a[0] = 1.0; a[1] = 2.0; a[2] = 3.0; + OpenMesh::Vec3f v(a); + EXPECT_EQ(OpenMesh::Vec3f(1.0, 2.0, 3.0), v); + + // This should not invoke the array constructor. + OpenMesh::Vec3d v2(3.0f); +} + +TEST_F(OpenMeshVectorTest, normalized_cond) { + OpenMesh::Vec3d v1(1, -2, 3), v2(0, 0, 0); + EXPECT_EQ(OpenMesh::Vec3d(0, 0, 0), v2.normalize_cond()); + const OpenMesh::Vec3d r1 = + OpenMesh::Vec3d( + 0.2672612419124244, + -0.5345224838248488, + 0.8017837257372732) - v1.normalize_cond(); + EXPECT_NEAR(r1[0], 0.0, 1e-12); + EXPECT_NEAR(r1[1], 0.0, 1e-12); + EXPECT_NEAR(r1[2], 0.0, 1e-12); +} + +TEST_F(OpenMeshVectorTest, size_dim) { + OpenMesh::Vec3d v3d(1, 2, 3); + OpenMesh::Vec3f v3f(1, 2, 3); + OpenMesh::Vec2i v2i(1, 2); + + EXPECT_EQ(3u, v3d.size()); + EXPECT_EQ(3, v3d.dim()); + EXPECT_EQ(3u, v3f.size()); + EXPECT_EQ(3, v3f.dim()); + EXPECT_EQ(2u, v2i.size()); + EXPECT_EQ(2, v2i.dim()); +} }