Rebased ref, commits from common ancestor:
commit 064d276bbc2ad50ae73a3ddc24c48a4d4936f25d
Author: Tomaž Vajngerl <[email protected]>
AuthorDate: Thu Nov 12 10:01:20 2020 +0100
Commit: Tomaž Vajngerl <[email protected]>
CommitDate: Fri Aug 19 16:11:54 2022 +0200
basegfx: added Length class as the base unit for length
Change-Id: I1d4790b60dd784e8b2e2e438274f3ebd6db4b60c
diff --git a/basegfx/CppunitTest_basegfx.mk b/basegfx/CppunitTest_basegfx.mk
index 88f4966262f2..98d3a5d41926 100644
--- a/basegfx/CppunitTest_basegfx.mk
+++ b/basegfx/CppunitTest_basegfx.mk
@@ -45,6 +45,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,basegfx,\
basegfx/test/clipstate \
basegfx/test/genericclipper \
basegfx/test/VectorTest \
+ 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..421e98934c90
--- /dev/null
+++ b/basegfx/test/LengthUnitTest.cxx
@@ -0,0 +1,135 @@
+/* -*- 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/Length.hxx>
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+class LengthTest : public CppUnit::TestFixture
+{
+public:
+ void testBasic()
+ {
+ 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());
+
+ auto aa = gfx::Length::hmm<sal_Int64>(10.1);
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(3600), aa.raw());
+
+ auto bb = gfx::Length::hmm<double>(10.1);
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(3636), bb.raw());
+ }
+
+ void testInRange()
+ {
+ gfx::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());
+ }
+
+ void testInTuple()
+ {
+ gfx::Tuple2DL aTuple(0.5_pt, 1_pt);
+ CPPUNIT_ASSERT_EQUAL(6350_emu, aTuple.getX());
+ CPPUNIT_ASSERT_EQUAL(12700_emu, aTuple.getY());
+ }
+
+ void testConversionToRectanle()
+ {
+ tools::Rectangle aRectangle(10, 20, 110, 120);
+ gfx::Range2DL aRange = gfx::length::fromRectangleHmm(aRectangle);
+ CPPUNIT_ASSERT_EQUAL(10_hmm, aRange.getMinX());
+ CPPUNIT_ASSERT_EQUAL(20_hmm, aRange.getMinY());
+ CPPUNIT_ASSERT_EQUAL(110_hmm, aRange.getMaxX());
+ CPPUNIT_ASSERT_EQUAL(120_hmm, aRange.getMaxY());
+
+ tools::Rectangle aRectangleConverted =
gfx::length::toRectangleHmm(aRange);
+ CPPUNIT_ASSERT_EQUAL(aRectangle, aRectangleConverted);
+ }
+
+ CPPUNIT_TEST_SUITE(LengthTest);
+ CPPUNIT_TEST(testBasic);
+ CPPUNIT_TEST(testInRange);
+ CPPUNIT_TEST(testInTuple);
+ CPPUNIT_TEST(testConversionToRectanle);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LengthTest);
diff --git a/include/basegfx/units/Length.hxx b/include/basegfx/units/Length.hxx
new file mode 100644
index 000000000000..64fd22260f83
--- /dev/null
+++ b/include/basegfx/units/Length.hxx
@@ -0,0 +1,110 @@
+/* -*- 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 <basegfx/units/LengthUnitBase.hxx>
+#include <basegfx/range/Range2D.hxx>
+
+#include <tools/gen.hxx>
+
+namespace gfx
+{
+typedef LengthUnitBase<sal_Int64> Length;
+
+struct LengthTraits
+{
+ static constexpr Length minVal() { return Length::min(); };
+ static constexpr Length maxVal() { return Length::max(); };
+ static constexpr Length neutral() { return Length(); };
+
+ typedef Length DifferenceType;
+};
+
+typedef basegfx::Range2D<gfx::Length, gfx::LengthTraits> Range2DL;
+typedef basegfx::Tuple2D<gfx::Length> Tuple2DL;
+
+namespace length
+{
+static inline Range2DL fromRectangleHmm(tools::Rectangle const& rRectangle)
+{
+ auto left = Length::hmm(rRectangle.Left());
+ auto top = Length::hmm(rRectangle.Top());
+ auto right = Length::hmm(rRectangle.Right());
+ auto bottom = Length::hmm(rRectangle.Bottom());
+ return Range2DL(left, top, right, bottom);
+}
+
+static inline tools::Rectangle toRectangleHmm(Range2DL const& rRange2D)
+{
+ auto left = rRange2D.getMinX().as_hmm();
+ auto top = rRange2D.getMinY().as_hmm();
+ auto right = rRange2D.getMaxX().as_hmm();
+ auto bottom = rRange2D.getMaxY().as_hmm();
+ return tools::Rectangle(left, top, right, bottom);
+}
+
+} // end namespace length
+
+} // 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); }
+
+constexpr gfx::Length operator"" _in(long double value)
+{
+ return gfx::Length::emu(std::round(gfx::constFactor_in_to_EMU * value));
+}
+constexpr gfx::Length operator"" _cm(long double value)
+{
+ return gfx::Length::emu(std::round(gfx::constFactor_cm_to_EMU * value));
+}
+
+constexpr gfx::Length operator"" _mm(long double value)
+{
+ return gfx::Length::emu(std::round(gfx::constFactor_mm_to_EMU * value));
+}
+
+constexpr gfx::Length operator"" _hmm(long double value)
+{
+ return gfx::Length::emu(std::round(gfx::constFactor_hmm_to_EMU * value));
+}
+
+constexpr gfx::Length operator"" _twip(long double value)
+{
+ return gfx::Length::emu(std::round(gfx::constFactor_twip_to_EMU * value));
+}
+
+constexpr gfx::Length operator"" _pt(long double value)
+{
+ return gfx::Length::emu(std::round(gfx::constFactor_pt_to_EMU * value));
+}
+
+constexpr gfx::Length operator"" _px(long double value)
+{
+ return gfx::Length::emu(std::round(gfx::constFactor_px_to_EMU * 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()
+ << ")";
+}
diff --git a/include/basegfx/units/LengthUnitBase.hxx
b/include/basegfx/units/LengthUnitBase.hxx
new file mode 100644
index 000000000000..c1d8a8e8e431
--- /dev/null
+++ b/include/basegfx/units/LengthUnitBase.hxx
@@ -0,0 +1,186 @@
+/* -*- 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>
+#include <cmath>
+
+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 TYPE> class LengthUnitBase
+{
+private:
+ // value in EMU units
+ TYPE m_nValue;
+
+ constexpr explicit LengthUnitBase(TYPE nValue)
+ : m_nValue(nValue)
+ {
+ }
+
+public:
+ static constexpr LengthUnitBase min() { return
LengthUnitBase(SAL_MIN_INT64); }
+
+ static constexpr LengthUnitBase max() { return
LengthUnitBase(SAL_MAX_INT64); }
+
+ template <typename INPUT_TYPE> static constexpr LengthUnitBase
cm(INPUT_TYPE nValue)
+ {
+ return LengthUnitBase(TYPE(gfx::constFactor_cm_to_EMU * nValue));
+ }
+
+ template <typename INPUT_TYPE> static constexpr LengthUnitBase
mm(INPUT_TYPE nValue)
+ {
+ return LengthUnitBase(TYPE(gfx::constFactor_mm_to_EMU * nValue));
+ }
+
+ template <typename INPUT_TYPE> static constexpr LengthUnitBase
hmm(INPUT_TYPE nValue)
+ {
+ return LengthUnitBase(TYPE(gfx::constFactor_hmm_to_EMU * nValue));
+ }
+
+ template <typename INPUT_TYPE> static constexpr LengthUnitBase
in(INPUT_TYPE nValue)
+ {
+ return LengthUnitBase(TYPE(gfx::constFactor_in_to_EMU * nValue));
+ }
+
+ template <typename INPUT_TYPE> static constexpr LengthUnitBase
twip(INPUT_TYPE nValue)
+ {
+ return LengthUnitBase(TYPE(gfx::constFactor_twip_to_EMU * nValue));
+ }
+
+ template <typename INPUT_TYPE> static constexpr LengthUnitBase
pt(INPUT_TYPE nValue)
+ {
+ return LengthUnitBase(TYPE(gfx::constFactor_pt_to_EMU * nValue));
+ }
+
+ template <typename INPUT_TYPE> static constexpr LengthUnitBase
px(INPUT_TYPE nValue)
+ {
+ return LengthUnitBase(TYPE(gfx::constFactor_px_to_EMU * nValue));
+ }
+
+ template <typename INPUT_TYPE> static constexpr LengthUnitBase
emu(INPUT_TYPE nValue)
+ {
+ return LengthUnitBase(TYPE(nValue));
+ }
+
+ constexpr explicit LengthUnitBase()
+ : m_nValue(0)
+ {
+ }
+
+ constexpr explicit operator TYPE() 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*=(TYPE const& rhs)
+ {
+ m_nValue *= rhs;
+ return *this;
+ }
+
+ constexpr LengthUnitBase& operator/=(TYPE 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;
+ }
+
+ constexpr TYPE 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;
+}
+
+} // end namespace gfx