Rebased ref, commits from common ancestor: commit 39abdba3d599bfe30c9fd21dd72f181bed046373 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Thu Nov 12 10:01:20 2020 +0100 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Mon May 16 15:11:37 2022 +0900
basegfx: added LengthUnit class as the base unit for length Change-Id: I1d4790b60dd784e8b2e2e438274f3ebd6db4b60c diff --git a/basegfx/CppunitTest_basegfx.mk b/basegfx/CppunitTest_basegfx.mk index d0a6739a34ef..a19e3d8b55f1 100644 --- a/basegfx/CppunitTest_basegfx.mk +++ b/basegfx/CppunitTest_basegfx.mk @@ -44,6 +44,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,basegfx,\ basegfx/test/basegfxtools \ basegfx/test/clipstate \ basegfx/test/genericclipper \ + basegfx/test/LengthUnitTest \ )) # vim: set noet sw=4 ts=4: diff --git a/basegfx/test/LengthUnitTest.cxx b/basegfx/test/LengthUnitTest.cxx new file mode 100644 index 000000000000..26c77c9884b0 --- /dev/null +++ b/basegfx/test/LengthUnitTest.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <basegfx/units/LengthUnit.hxx> +#include <basegfx/range/Range2D.hxx> + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +struct LengthTraits +{ + static constexpr gfx::Length minVal() { return gfx::Length::min(); }; + static constexpr gfx::Length maxVal() { return gfx::Length::max(); }; + static constexpr gfx::Length neutral() { return gfx::Length(); }; + + typedef gfx::Length DifferenceType; +}; + +class LengthTest : public CppUnit::TestFixture +{ +public: + void test() + { + gfx::Length cm = 1_cm + 5_cm - 2_cm; + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, cm.as_cm(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.04, cm.as_m(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(40.0, cm.as_mm(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(1440000), cm.raw()); + + gfx::Length cm2 = 5_cm * 2; + CPPUNIT_ASSERT_EQUAL(sal_Int64(3600000), cm2.raw()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, cm2.as_cm(), 1e-4); + + // 1 km - 50 m = 950 m = 95000 cm + gfx::Length cm3 = 100000_cm - 5000_cm; + CPPUNIT_ASSERT_EQUAL(sal_Int64(34200000000), cm3.raw()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(95000.0, cm3.as_cm(), 1e-4); + + gfx::Length cm4(1_cm); + cm4 /= 2; + CPPUNIT_ASSERT_EQUAL(sal_Int64(180000), cm4.raw()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, cm4.as_cm(), 1e-4); + + // (635 * 20) + 3 * (635 * 15) = 41275EMU + gfx::Length pt = 1_pt + 3_px; + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.25, pt.as_pt(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(65.0, pt.as_twip(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0451, pt.as_in(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(41275), pt.raw()); + + gfx::Length inch = 1_in; // 1440 * 635 + CPPUNIT_ASSERT_DOUBLES_EQUAL(1440.0, inch.as_twip(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(96.0, inch.as_px(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, inch.as_in(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(914400.0, inch.as_emu(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(914400), inch.raw()); + + // Conversion + sal_Int64 asNumber(17_pt); + asNumber += sal_Int64(1_pt); + gfx::Length asLength = gfx::Length::emu(asNumber); + CPPUNIT_ASSERT_EQUAL(sal_Int64(18 * 635 * 20), asLength.raw()); + + gfx::Length maximum = gfx::Length::emu(SAL_MAX_INT64); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256204778801.5, maximum.as_m(), 1e-1); + // 256204778 km + CPPUNIT_ASSERT_EQUAL(sal_Int64(SAL_MAX_INT64), maximum.raw()); + + gfx::Length minimum = gfx::Length::emu(SAL_MIN_INT64); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-256204778801.5, minimum.as_m(), 1e-1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(SAL_MIN_INT64), minimum.as_emu(), 1e-1); + CPPUNIT_ASSERT_EQUAL(sal_Int64(SAL_MIN_INT64), minimum.raw()); + + // 27 emu + 33 emu + 360 emu = 420 + gfx::Length emus = 27_emu + 33_emu + 1_hmm; + CPPUNIT_ASSERT_EQUAL(sal_Int64(420), emus.raw()); + + // Creation from number + int number = 10; + auto asCm = gfx::Length::cm(number); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, asCm.as_cm(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(3600000), asCm.raw()); + + auto asMm = gfx::Length::mm(number); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, asMm.as_mm(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(360000), asMm.raw()); + + auto asInch = gfx::Length::in(number); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, asInch.as_in(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(9144000), asInch.raw()); + + typedef gfx::Range2D<gfx::Length, LengthTraits> Range2DL; + + Range2DL aRange(1_cm, 2_cm, 2_cm, 30_mm); + CPPUNIT_ASSERT_EQUAL(1_cm, aRange.getMinX()); + CPPUNIT_ASSERT_EQUAL(2_cm, aRange.getMaxX()); + CPPUNIT_ASSERT_EQUAL(2_cm, aRange.getMinY()); + CPPUNIT_ASSERT_EQUAL(3_cm, aRange.getMaxY()); + + CPPUNIT_ASSERT_EQUAL(1_cm, aRange.getWidth()); + CPPUNIT_ASSERT_EQUAL(10_mm, aRange.getHeight()); + + gfx::Tuple2D<gfx::Length> aTuple(1_cm, 1_cm); + } + + CPPUNIT_TEST_SUITE(LengthTest); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(LengthTest); diff --git a/include/basegfx/units/LengthUnit.hxx b/include/basegfx/units/LengthUnit.hxx new file mode 100644 index 000000000000..1249b00b5a80 --- /dev/null +++ b/include/basegfx/units/LengthUnit.hxx @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <sal/types.h> + +#include <ostream> + +namespace gfx +{ +namespace +{ +constexpr sal_Int64 constFactor_hmm_to_EMU = 360ll; +constexpr sal_Int64 constFactor_mm_to_EMU = constFactor_hmm_to_EMU * 100ll; +constexpr sal_Int64 constFactor_cm_to_EMU = constFactor_hmm_to_EMU * 1000ll; +constexpr sal_Int64 constFactor_m_to_EMU = constFactor_hmm_to_EMU * 100000ll; + +constexpr sal_Int64 constFactor_twip_to_EMU = 635ll; +constexpr sal_Int64 constFactor_in_to_EMU = constFactor_twip_to_EMU * 1440ll; +constexpr sal_Int64 constFactor_pt_to_EMU = constFactor_twip_to_EMU * 20ll; +constexpr sal_Int64 constFactor_px_to_EMU = constFactor_twip_to_EMU * 15ll; + +} // end anonymous namespace + +template <typename T> class LengthUnitBase +{ +private: + // value in EMU units + T m_nValue; + + constexpr explicit LengthUnitBase(T nValue) + : m_nValue(nValue) + { + } + +public: + static constexpr LengthUnitBase min() { return LengthUnitBase(SAL_MIN_INT64); } + + static constexpr LengthUnitBase max() { return LengthUnitBase(SAL_MAX_INT64); } + + static constexpr LengthUnitBase cm(T nValue) + { + return LengthUnitBase(gfx::constFactor_cm_to_EMU * nValue); + } + + static constexpr LengthUnitBase mm(T nValue) + { + return LengthUnitBase(gfx::constFactor_mm_to_EMU * nValue); + } + + static constexpr LengthUnitBase hmm(T nValue) + { + return LengthUnitBase(gfx::constFactor_hmm_to_EMU * nValue); + } + + static constexpr LengthUnitBase in(T nValue) + { + return LengthUnitBase(gfx::constFactor_in_to_EMU * nValue); + } + + static constexpr LengthUnitBase twip(T nValue) + { + return LengthUnitBase(gfx::constFactor_twip_to_EMU * nValue); + } + + static constexpr LengthUnitBase pt(T nValue) + { + return LengthUnitBase(gfx::constFactor_pt_to_EMU * nValue); + } + + static constexpr LengthUnitBase px(T nValue) + { + return LengthUnitBase(gfx::constFactor_px_to_EMU * nValue); + } + + static constexpr LengthUnitBase emu(T nValue) { return LengthUnitBase(nValue); } + + constexpr explicit LengthUnitBase() + : m_nValue(0) + { + } + + constexpr explicit operator T() const { return m_nValue; } + + constexpr LengthUnitBase& operator+=(LengthUnitBase const& rhs) + { + m_nValue += rhs.m_nValue; + return *this; + } + + constexpr LengthUnitBase& operator-=(LengthUnitBase const& rhs) + { + m_nValue -= rhs.m_nValue; + return *this; + } + + constexpr LengthUnitBase& operator*=(T const& rhs) + { + m_nValue *= rhs; + return *this; + } + + constexpr LengthUnitBase& operator/=(T const& rhs) + { + m_nValue /= rhs; + return *this; + } + + constexpr LengthUnitBase& operator-() + { + m_nValue = -m_nValue; + return *this; + } + + constexpr bool operator<(LengthUnitBase const& other) const + { + return m_nValue < other.m_nValue; + } + constexpr bool operator<=(LengthUnitBase const& other) const + { + return m_nValue <= other.m_nValue; + } + constexpr bool operator>(LengthUnitBase const& other) const + { + return m_nValue > other.m_nValue; + } + constexpr bool operator>=(LengthUnitBase const& other) const + { + return m_nValue >= other.m_nValue; + } + constexpr bool operator==(LengthUnitBase const& other) const + { + return m_nValue == other.m_nValue; + } + constexpr bool operator!=(LengthUnitBase const& other) const + { + return m_nValue != other.m_nValue; + } + + T raw() const { return m_nValue; } + + double as_hmm() const { return m_nValue / double(constFactor_hmm_to_EMU); } + double as_mm() const { return m_nValue / double(constFactor_mm_to_EMU); } + double as_cm() const { return m_nValue / double(constFactor_cm_to_EMU); } + double as_m() const { return m_nValue / double(constFactor_m_to_EMU); } + double as_twip() const { return m_nValue / double(constFactor_twip_to_EMU); } + double as_in() const { return m_nValue / double(constFactor_in_to_EMU); } + double as_pt() const { return m_nValue / double(constFactor_pt_to_EMU); } + double as_px() const { return m_nValue / double(constFactor_px_to_EMU); } + double as_emu() const { return double(m_nValue); } +}; + +template <typename T> +inline LengthUnitBase<T> operator+(LengthUnitBase<T> lhs, const LengthUnitBase<T>& rhs) +{ + return lhs += rhs; +} + +template <typename T> +inline LengthUnitBase<T> operator-(LengthUnitBase<T> lhs, const LengthUnitBase<T>& rhs) +{ + return lhs -= rhs; +} + +template <typename T> inline LengthUnitBase<T> operator*(LengthUnitBase<T> lhs, const long rhs) +{ + return lhs *= rhs; +} + +template <typename T> inline LengthUnitBase<T> operator/(LengthUnitBase<T> lhs, const long rhs) +{ + return lhs /= rhs; +} + +typedef LengthUnitBase<sal_Int64> Length; +typedef LengthUnitBase<double> LengthD; + +} // end namespace gfx + +constexpr gfx::Length operator"" _emu(unsigned long long value) { return gfx::Length::emu(value); } + +constexpr gfx::Length operator"" _in(unsigned long long value) { return gfx::Length::in(value); } + +constexpr gfx::Length operator"" _cm(unsigned long long value) { return gfx::Length::cm(value); } + +constexpr gfx::Length operator"" _mm(unsigned long long value) { return gfx::Length::mm(value); } + +constexpr gfx::Length operator"" _hmm(unsigned long long value) { return gfx::Length::hmm(value); } + +constexpr gfx::Length operator"" _twip(unsigned long long value) +{ + return gfx::Length::twip(value); +} + +constexpr gfx::Length operator"" _pt(unsigned long long value) { return gfx::Length::pt(value); } + +constexpr gfx::Length operator"" _px(unsigned long long value) { return gfx::Length::px(value); } + +/** Write to char stream */ +template <typename charT, typename traits> +inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& stream, + const gfx::Length& rLength) +{ + return stream << rLength.raw() << " (twip=" << rLength.as_twip() << ", hmm=" << rLength.as_hmm() + << ")"; +}