diff --git a/src/opentime/rationalTime.h b/src/opentime/rationalTime.h index fbdd02d4ec..373324454c 100644 --- a/src/opentime/rationalTime.h +++ b/src/opentime/rationalTime.h @@ -77,6 +77,34 @@ class RationalTime return fabs(value_rescaled_to(other._rate) - other._value) <= delta; } + // Return whether the value and rate are equal to another RationalTime. + // This is different from the operator "==" that rescales the time before + // comparison. + constexpr bool strictly_equal(RationalTime other) const noexcept + { + return _value == other._value && _rate == other._rate; + } + + // Return a RationalTime with the largest integer value not greater than + // this value. + RationalTime floor() const + { + return RationalTime{ std::floor(_value), _rate }; + } + + // Return a RationalTime with the smallest integer value not less than + // this value. + RationalTime ceil() const + { + return RationalTime{ std::ceil(_value), _rate }; + } + + // Return a RationalTime with the nearest integer value to this value. + RationalTime round() const + { + return RationalTime{ std::round(_value), _rate }; + } + static RationalTime constexpr duration_from_start_end_time( RationalTime start_time, RationalTime end_time_exclusive) noexcept @@ -263,11 +291,6 @@ class RationalTime static RationalTime _invalid_time; static constexpr double _invalid_rate = -1; - RationalTime _floor() const noexcept - { - return RationalTime{ floor(_value), _rate }; - } - friend class TimeTransform; friend class TimeRange; diff --git a/src/opentime/timeRange.h b/src/opentime/timeRange.h index 3c039c6da4..e4cad571cf 100644 --- a/src/opentime/timeRange.h +++ b/src/opentime/timeRange.h @@ -61,8 +61,8 @@ class TimeRange if ((et - _start_time.rescaled_to(_duration))._value > 1) { - return _duration._value != floor(_duration._value) - ? et._floor() + return _duration._value != std::floor(_duration._value) + ? et.floor() : et - RationalTime(1, _duration._rate); } else diff --git a/src/py-opentimelineio/opentime-bindings/opentime_rationalTime.cpp b/src/py-opentimelineio/opentime-bindings/opentime_rationalTime.cpp index 776467c1ae..a311b5d0a2 100644 --- a/src/py-opentimelineio/opentime-bindings/opentime_rationalTime.cpp +++ b/src/py-opentimelineio/opentime-bindings/opentime_rationalTime.cpp @@ -81,6 +81,10 @@ or if the rate is less than or equal to zero. .def("value_rescaled_to", (double (RationalTime::*)(RationalTime) const) &RationalTime::value_rescaled_to, "other"_a) .def("almost_equal", &RationalTime::almost_equal, "other"_a, "delta"_a = 0) + .def("strictly_equal", &RationalTime::strictly_equal, "other"_a) + .def("floor", &RationalTime::floor) + .def("ceil", &RationalTime::ceil) + .def("round", &RationalTime::round) .def("__copy__", [](RationalTime rt) { return rt; }) diff --git a/tests/test_opentime.cpp b/tests/test_opentime.cpp index b508da8993..2569522a42 100644 --- a/tests/test_opentime.cpp +++ b/tests/test_opentime.cpp @@ -27,6 +27,8 @@ main(int argc, char** argv) assertEqual(t1, t1); otime::RationalTime t2(30.2); assertEqual(t1, t2); + otime::RationalTime t3(60.4, 2.0); + assertEqual(t1, t3); }); tests.add_test("test_inequality", [] { @@ -38,6 +40,26 @@ main(int argc, char** argv) assertFalse(t1 != t3); }); + tests.add_test("test_strict_equality", [] { + otime::RationalTime t1(30.2); + assertTrue(t1.strictly_equal(t1)); + otime::RationalTime t2(30.2); + assertTrue(t1.strictly_equal(t2)); + otime::RationalTime t3(60.4, 2.0); + assertFalse(t1.strictly_equal(t3)); + }); + + tests.add_test("test_rounding", [] { + otime::RationalTime t1(30.2); + assertEqual(t1.floor(), otime::RationalTime(30.0)); + assertEqual(t1.ceil(), otime::RationalTime(31.0)); + assertEqual(t1.round(), otime::RationalTime(30.0)); + otime::RationalTime t2(30.8); + assertEqual(t2.floor(), otime::RationalTime(30.0)); + assertEqual(t2.ceil(), otime::RationalTime(31.0)); + assertEqual(t2.round(), otime::RationalTime(31.0)); + }); + tests.add_test("test_from_time_string", [] { std::string time_string = "0:12:04"; auto t = otime::RationalTime(24 * (12 * 60 + 4), 24); diff --git a/tests/test_opentime.py b/tests/test_opentime.py index 5afc847b71..81f03bd7f8 100755 --- a/tests/test_opentime.py +++ b/tests/test_opentime.py @@ -32,6 +32,8 @@ def test_equality(self): t2 = otio.opentime.RationalTime(30.2) self.assertTrue(t1 is not t2) self.assertEqual(t1, t2) + t3 = otio.opentime.RationalTime(60.4, 2.0) + self.assertEqual(t1, t3) def test_inequality(self): t1 = otio.opentime.RationalTime(30.2) @@ -43,6 +45,24 @@ def test_inequality(self): self.assertTrue(t1 is not t3) self.assertFalse(t1 != t3) + def test_strict_equality(self): + t1 = otio.opentime.RationalTime(30.2) + self.assertTrue(t1.strictly_equal(t1)) + t2 = otio.opentime.RationalTime(30.2) + self.assertTrue(t1.strictly_equal(t2)) + t3 = otio.opentime.RationalTime(60.4, 2.0) + self.assertFalse(t1.strictly_equal(t3)) + + def test_rounding(self): + t1 = otio.opentime.RationalTime(30.2) + self.assertEqual(t1.floor(), otio.opentime.RationalTime(30.0)) + self.assertEqual(t1.ceil(), otio.opentime.RationalTime(31.0)) + self.assertEqual(t1.round(), otio.opentime.RationalTime(30.0)) + t2 = otio.opentime.RationalTime(30.8) + self.assertEqual(t2.floor(), otio.opentime.RationalTime(30.0)) + self.assertEqual(t2.ceil(), otio.opentime.RationalTime(31.0)) + self.assertEqual(t2.round(), otio.opentime.RationalTime(31.0)) + def test_comparison(self): t1 = otio.opentime.RationalTime(15.2) t2 = otio.opentime.RationalTime(15.6)