From c97f3e41f7bc21ff94b71c6d844d5fa22281f33c Mon Sep 17 00:00:00 2001 From: Marcos Wagner Date: Thu, 12 Aug 2021 13:31:28 -0300 Subject: [PATCH 1/5] Adds Line2, SignalStats, Temperature python interface Signed-off-by: Marcos Wagner --- include/ignition/math/Line2.hh | 4 +- src/Line2.i | 82 +++++ src/Line2_TEST.py | 215 ++++++++++++++ src/SignalStats.i | 100 +++++++ src/SignalStats_TEST.py | 528 +++++++++++++++++++++++++++++++++ src/Temperature.i | 87 ++++++ src/Temperature_TEST.py | 130 ++++++++ src/python/python.i | 4 + 8 files changed, 1148 insertions(+), 2 deletions(-) create mode 100644 src/Line2.i create mode 100644 src/Line2_TEST.py create mode 100644 src/SignalStats.i create mode 100644 src/SignalStats_TEST.py create mode 100644 src/Temperature.i create mode 100644 src/Temperature_TEST.py diff --git a/include/ignition/math/Line2.hh b/include/ignition/math/Line2.hh index fa7b5de74..0b669f868 100644 --- a/include/ignition/math/Line2.hh +++ b/include/ignition/math/Line2.hh @@ -110,7 +110,7 @@ namespace ignition double _epsilon = 1e-6) const { return math::equal(this->CrossProduct(_pt), - static_cast(0), _epsilon); + 0., _epsilon); } /// \brief Check if the given line is parallel with this line. @@ -124,7 +124,7 @@ namespace ignition double _epsilon = 1e-6) const { return math::equal(this->CrossProduct(_line), - static_cast(0), _epsilon); + 0., _epsilon); } /// \brief Check if the given line is collinear with this line. This diff --git a/src/Line2.i b/src/Line2.i new file mode 100644 index 000000000..196fdee93 --- /dev/null +++ b/src/Line2.i @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +%module line2 +%{ +#include +#include +#include +%} + +%include "std_string.i" + +namespace ignition +{ + namespace math + { + template + class Line2 + { + public: Line2(const math::Vector2 &_ptA, const math::Vector2 &_ptB); + public: Line2(double _x1, double _y1, double _x2, double _y2); + public: void Set(const math::Vector2 &_ptA, + const math::Vector2 &_ptB); + public: void Set(double _x1, double _y1, double _x2, double _y2); + public: double CrossProduct(const Line2 &_line) const; + public: double CrossProduct(const Vector2 &_pt) const; + public: bool Collinear(const math::Vector2 &_pt, + double _epsilon = 1e-6) const; + public: bool Parallel(const math::Line2 &_line, + double _epsilon = 1e-6) const; + public: bool Collinear(const math::Line2 &_line, + double _epsilon = 1e-6) const; + public: bool OnSegment(const math::Vector2 &_pt, + double _epsilon = 1e-6) const; + public: bool Within(const math::Vector2 &_pt, + double _epsilon = 1e-6) const; + public: bool Intersect(const Line2 &_line, + double _epsilon = 1e-6) const; + public: bool Intersect(const Line2 &_line, math::Vector2 &_pt, + double _epsilon = 1e-6) const; + public: T Length() const; + public: double Slope() const; + public: bool operator==(const Line2 &_line) const; + public: bool operator!=(const Line2 &_line) const; + }; + + %extend Line2 + { + ignition::math::Vector2 __getitem__(unsigned int i) const + { + return (*$self)[i]; + } + } + + %extend Line2 + { + std::string __str__() const { + std::ostringstream out; + out << *$self; + return out.str(); + } + } + + %template(Line2i) Line2; + %template(Line2d) Line2; + %template(Line2f) Line2; + } +} diff --git a/src/Line2_TEST.py b/src/Line2_TEST.py new file mode 100644 index 000000000..13351b1a1 --- /dev/null +++ b/src/Line2_TEST.py @@ -0,0 +1,215 @@ +# Copyright (C) 2021 Open Source Robotics Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import math +from ignition.math import Line2d +from ignition.math import Vector2d + + +class TestLine2d(unittest.TestCase): + + def test_construction(self): + lineA = Line2d(0, 0, 10, 10) + self.assertAlmostEqual(lineA[0].X(), 0.0) + self.assertAlmostEqual(lineA[0].Y(), 0.0) + self.assertAlmostEqual(lineA[1].X(), 10.0) + self.assertAlmostEqual(lineA[1].Y(), 10.0) + + lineB = Line2d(Vector2d(1, 2), Vector2d(3, 4)) + self.assertAlmostEqual(lineB[0].X(), 1.0) + self.assertAlmostEqual(lineB[0].Y(), 2.0) + self.assertAlmostEqual(lineB[1].X(), 3.0) + self.assertAlmostEqual(lineB[1].Y(), 4.0) + + self.assertAlmostEqual(lineB[2].X(), lineB[1].X()) + + def test_length(self): + lineA = Line2d(0, 0, 10, 10) + self.assertAlmostEqual(lineA.Length(), math.sqrt(200), delta=1e-10) + + def test_slope(self): + line = Line2d(0, 0, 10, 10) + self.assertAlmostEqual(line.Slope(), 1.0, delta=1e-10) + + line = Line2d(0, 0, 0, 10) + self.assertTrue(math.isnan(line.Slope())) + + line = Line2d(-10, 0, 100, 0) + self.assertAlmostEqual(line.Slope(), 0.0) + + def test_parallel_line(self): + # Line is always parallel with itself + line = Line2d(0, 0, 10, 0) + self.assertTrue(line.Parallel(line, 1e-10)) + + # Degenerate line segment + # Still expect Line is parallel with itself + line = Line2d(0, 0, 0, 0) + self.assertTrue(line.Parallel(line, 1e-10)) + + lineA = Line2d(0, 0, 10, 0) + lineB = Line2d(0, 0, 10, 0) + self.assertTrue(lineA.Parallel(lineB, 1e-10)) + + lineB.Set(0, 0, 0, 10) + self.assertFalse(lineA.Parallel(lineB)) + + lineB.Set(0, 10, 10, 10) + self.assertTrue(lineA.Parallel(lineB)) + + lineB.Set(0, 10, 10, 10.00001) + self.assertFalse(lineA.Parallel(lineB, 1e-10)) + self.assertFalse(lineA.Parallel(lineB)) + self.assertTrue(lineA.Parallel(lineB, 1e-3)) + + def test_collinear_line(self): + # Line is always collinear with itself + line = Line2d(0, 0, 10, 0) + self.assertTrue(line.Collinear(line, 1e-10)) + + lineA = Line2d(0, 0, 10, 0) + lineB = Line2d(0, 0, 10, 0) + self.assertTrue(lineA.Collinear(lineB, 1e-10)) + + lineB.Set(0, 10, 10, 10) + self.assertFalse(lineA.Collinear(lineB)) + + lineB.Set(9, 0, 10, 0.00001) + self.assertFalse(lineA.Collinear(lineB, 1e-10)) + self.assertFalse(lineA.Collinear(lineB)) + self.assertTrue(lineA.Collinear(lineB, 1e-3)) + + def test_collinear_point(self): + lineA = Line2d(0, 0, 10, 0) + pt = Vector2d(0, 0) + self.assertTrue(lineA.Collinear(pt)) + + ptLine = Line2d(pt, pt) + self.assertTrue(lineA.Collinear(ptLine)) + + pt.Set(1000, 0) + self.assertTrue(lineA.Collinear(pt, 1e-10)) + + ptLine = Line2d(pt, pt) + self.assertTrue(lineA.Parallel(ptLine)) + self.assertFalse(lineA.Intersect(ptLine)) + self.assertFalse(lineA.Collinear(ptLine, 1e-10)) + + pt.Set(10, 0) + ptLine.Set(pt, pt) + self.assertTrue(lineA.Collinear(ptLine, 1e-10)) + + pt.Set(0, 0.00001) + self.assertFalse(lineA.Collinear(pt)) + self.assertTrue(lineA.Collinear(pt, 1e-3)) + + ptLine = Line2d(pt, pt) + self.assertFalse(lineA.Collinear(ptLine)) + self.assertTrue(lineA.Parallel(ptLine)) + self.assertFalse(lineA.Intersect(ptLine)) + self.assertTrue(lineA.Intersect(ptLine, 1e-2)) + self.assertTrue(lineA.Collinear(ptLine, 1e-3)) + + pt.Set(0, -0.00001) + self.assertFalse(lineA.Collinear(pt)) + self.assertTrue(lineA.Collinear(pt, 1e-3)) + + ptLine = Line2d(pt, pt) + self.assertFalse(lineA.Collinear(ptLine)) + self.assertTrue(lineA.Collinear(ptLine, 1e-4)) + + def test_intersect(self): + pt = Vector2d() + + # Parallel horizontal lines + lineA = Line2d(1, 1, 2, 1) + lineB = Line2d(1, 2, 2, 2) + self.assertFalse(lineA.Intersect(lineB, pt)) + + # Parallel vertical lines + lineA.Set(1, 1, 1, 10) + lineB.Set(2, 1, 2, 10) + self.assertFalse(lineA.Intersect(lineB, pt)) + + # Two lines that form an inverted T with a gap + lineA.Set(1, 1, 1, 10) + lineB.Set(0, 0, 2, 0) + self.assertFalse(lineA.Intersect(lineB, pt)) + + # Two lines that form a T with a gap + lineA.Set(1, 1, 1, 10) + lineB.Set(0, 10.1, 2, 10.1) + self.assertFalse(lineA.Intersect(lineB, pt)) + + # Two lines that form an inverted T with a gap + lineA.Set(0, -10, 0, 10) + lineB.Set(1, 0, 10, 0) + self.assertFalse(lineA.Intersect(lineB, pt)) + + # Two lines that form a T with a gap + lineA.Set(0, -10, 0, 10) + lineB.Set(-1, 0, -10, 0) + self.assertFalse(lineA.Intersect(lineB, pt)) + + # Two collinear lines, one starts where the other stopped + lineA.Set(1, 1, 1, 10) + lineB.Set(1, 10, 1, 11) + self.assertTrue(lineA.Intersect(lineB, pt)) + self.assertEqual(pt, Vector2d(1, 10)) + + # Two collinear lines, one overlaps the other + lineA.Set(0, 0, 0, 10) + lineB.Set(0, 9, 0, 11) + self.assertTrue(lineA.Intersect(lineB, pt)) + self.assertEqual(pt, Vector2d(0, 9)) + + # Two collinear lines, one overlaps the other + lineA.Set(0, 0, 0, 10) + lineB.Set(0, -10, 0, 1) + self.assertTrue(lineA.Intersect(lineB, pt)) + self.assertEqual(pt, Vector2d(0, 1)) + + # Two intersecting lines + lineA.Set(0, 0, 10, 10) + lineB.Set(0, 10, 10, 0) + self.assertTrue(lineA.Intersect(lineB, pt)) + self.assertEqual(pt, Vector2d(5, 5)) + + def test_equality(self): + lineA = Line2d(1, 1, 2, 1) + lineB = Line2d(1, 2, 2, 2) + + self.assertTrue(lineA != lineB) + self.assertTrue(lineA == lineA) + + lineB.Set(1, 1, 2, 1.1) + self.assertFalse(lineA == lineB) + + lineB.Set(1, 1, 2.1, 1) + self.assertFalse(lineA == lineB) + + lineB.Set(1, 1.1, 2, 1) + self.assertFalse(lineA == lineB) + + lineB.Set(1.1, 1, 2, 1) + self.assertFalse(lineA == lineB) + + def test_serialization(self): + line = Line2d(0, 1, 2, 3) + self.assertEqual(str(line), "0 1 2 3") + + +if __name__ == '__main__': + unittest.main() diff --git a/src/SignalStats.i b/src/SignalStats.i new file mode 100644 index 000000000..521ba063d --- /dev/null +++ b/src/SignalStats.i @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +%module signalStats + +%{ +#include +%} + +%include "std_string.i" +%include "std_map.i" +%template(map_string_double) std::map; + +namespace ignition +{ + namespace math + { + class SignalStatistic + { + public: SignalStatistic(); + public: virtual ~SignalStatistic(); + public: SignalStatistic(const SignalStatistic &_ss); + public: virtual double Value() const = 0; + public: virtual std::string ShortName() const = 0; + public: virtual size_t Count() const; + public: virtual void InsertData(const double _data) = 0; + public: virtual void Reset(); + }; + + class SignalMaximum : public SignalStatistic + { + public: virtual double Value() const override; + public: virtual std::string ShortName() const override; + public: virtual void InsertData(const double _data) override; + }; + + class SignalMean : public SignalStatistic + { + public: virtual double Value() const override; + public: virtual std::string ShortName() const override; + public: virtual void InsertData(const double _data) override; + }; + + class SignalMinimum : public SignalStatistic + { + public: virtual double Value() const override; + public: virtual std::string ShortName() const override; + public: virtual void InsertData(const double _data) override; + }; + + class SignalRootMeanSquare : public SignalStatistic + { + public: virtual double Value() const override; + public: virtual std::string ShortName() const override; + public: virtual void InsertData(const double _data) override; + }; + + class SignalMaxAbsoluteValue : public SignalStatistic + { + public: virtual double Value() const override; + public: virtual std::string ShortName() const override; + public: virtual void InsertData(const double _data) override; + }; + + class SignalVariance : public SignalStatistic + { + public: virtual double Value() const override; + public: virtual std::string ShortName() const override; + public: virtual void InsertData(const double _data) override; + }; + + class SignalStats + { + public: SignalStats(); + public: ~SignalStats(); + public: SignalStats(const SignalStats &_ss); + public: size_t Count() const; + public: std::map Map() const; + public: void InsertData(const double _data); + public: bool InsertStatistic(const std::string &_name); + public: bool InsertStatistics(const std::string &_names); + public: void Reset(); + }; + + } +} \ No newline at end of file diff --git a/src/SignalStats_TEST.py b/src/SignalStats_TEST.py new file mode 100644 index 000000000..51d88235c --- /dev/null +++ b/src/SignalStats_TEST.py @@ -0,0 +1,528 @@ +# Copyright (C) 2021 Open Source Robotics Foundation + +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import math +from ignition.math import Rand +from ignition.math import SignalMaximum +from ignition.math import SignalMinimum +from ignition.math import SignalMean +from ignition.math import SignalVariance +from ignition.math import SignalRootMeanSquare +from ignition.math import SignalMaxAbsoluteValue +from ignition.math import SignalStats +from ignition.math import SignalStatistic + + +class TestSignalStats(unittest.TestCase): + + def test_signal_maximum_constructor(self): + # Constructor + max = SignalMaximum() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + self.assertEqual(max.ShortName(), "max") + + # Reset + max.Reset() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + def test_signal_maximum_constant_values(self): + # Constant values, max should match + max = SignalMaximum() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + max.InsertData(value) + self.assertAlmostEqual(max.Value(), value) + self.assertEqual(max.Count(), i) + + # Reset + max.Reset() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + def test_signal_maximum_alternating_values(self): + # Values with alternating sign, increasing magnitude + # Should always match positive value + max = SignalMaximum() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + + for i in range(1, 11): + max.InsertData(value * i) + self.assertAlmostEqual(max.Value(), value * i) + max.InsertData(-value * i) + self.assertAlmostEqual(max.Value(), value * i) + self.assertEqual(max.Count(), i*2) + + # Reset + max.Reset() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + def test_signal_mean_constructor(self): + # Constructor + mean = SignalMean() + self.assertAlmostEqual(mean.Value(), 0.0) + self.assertEqual(mean.Count(), 0) + self.assertEqual(mean.ShortName(), "mean") + + # Reset + mean.Reset() + self.assertAlmostEqual(mean.Value(), 0.0) + self.assertEqual(mean.Count(), 0) + + def test_signal_mean_constant_values(self): + # Constant values, mean should match + mean = SignalMean() + self.assertAlmostEqual(mean.Value(), 0.0) + self.assertEqual(mean.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + mean.InsertData(value) + self.assertAlmostEqual(mean.Value(), value) + self.assertEqual(mean.Count(), i) + + # Reset + mean.Reset() + self.assertAlmostEqual(mean.Value(), 0.0) + self.assertEqual(mean.Count(), 0) + + def test_signal_mean_alternating_values(self): + # Values with alternating sign, increasing magnitude + # Should be zero every other time + mean = SignalMean() + self.assertAlmostEqual(mean.Value(), 0.0) + self.assertEqual(mean.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + mean.InsertData(value * i) + mean.InsertData(-value * i) + self.assertAlmostEqual(mean.Value(), 0.0) + self.assertEqual(mean.Count(), i*2) + + # Reset + mean.Reset() + self.assertAlmostEqual(mean.Value(), 0.0) + self.assertEqual(mean.Count(), 0) + + def test_signal_minimum_constructor(self): + # Constructor + min = SignalMinimum() + self.assertAlmostEqual(min.Value(), 0.0) + self.assertEqual(min.Count(), 0) + self.assertEqual(min.ShortName(), "min") + + # Reset + min.Reset() + self.assertAlmostEqual(min.Value(), 0.0) + self.assertEqual(min.Count(), 0) + + def test_signal_minimum_constant_values(self): + # Constant values, min should match + min = SignalMinimum() + self.assertAlmostEqual(min.Value(), 0.0) + self.assertEqual(min.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + min.InsertData(value) + self.assertAlmostEqual(min.Value(), value) + self.assertEqual(min.Count(), i) + + # Reset + min.Reset() + self.assertAlmostEqual(min.Value(), 0.0) + self.assertEqual(min.Count(), 0) + + def test_signal_minimum_alternating_values(self): + # Values with alternating sign, increasing magnitude + # Should always match negative value + min = SignalMinimum() + self.assertAlmostEqual(min.Value(), 0.0) + self.assertEqual(min.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + min.InsertData(value * i) + min.InsertData(-value * i) + self.assertAlmostEqual(min.Value(), -value * i) + self.assertEqual(min.Count(), i*2) + + # Reset + min.Reset() + self.assertAlmostEqual(min.Value(), 0.0) + self.assertEqual(min.Count(), 0) + + def test_signal_root_mean_square(self): + # Constructor + rms = SignalRootMeanSquare() + self.assertAlmostEqual(rms.Value(), 0.0) + self.assertEqual(rms.Count(), 0) + self.assertEqual(rms.ShortName(), "rms") + + # Reset + rms.Reset() + self.assertAlmostEqual(rms.Value(), 0.0) + self.assertEqual(rms.Count(), 0) + + def test_signal_root_mean_square_constant_values(self): + # Constant values, rms should match + rms = SignalRootMeanSquare() + self.assertAlmostEqual(rms.Value(), 0.0) + self.assertEqual(rms.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + rms.InsertData(value) + self.assertAlmostEqual(rms.Value(), value) + self.assertEqual(rms.Count(), i) + + # Reset + rms.Reset() + self.assertAlmostEqual(rms.Value(), 0.0) + self.assertEqual(rms.Count(), 0) + + def test_signal_root_mean_square_alternating_values(self): + # Values with alternating sign, same magnitude + # rms should match absolute value every time + rms = SignalRootMeanSquare() + self.assertAlmostEqual(rms.Value(), 0.0) + self.assertEqual(rms.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + rms.InsertData(value) + self.assertAlmostEqual(rms.Value(), value) + self.assertEqual(rms.Count(), i*2-1) + + rms.InsertData(-value) + self.assertAlmostEqual(rms.Value(), value) + self.assertEqual(rms.Count(), i*2) + + # Reset + rms.Reset() + self.assertAlmostEqual(rms.Value(), 0.0) + self.assertEqual(rms.Count(), 0) + + def test_signal_max_absolute_value_constructor(self): + # Constructor + max = SignalMaxAbsoluteValue() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + self.assertEqual(max.ShortName(), "maxAbs") + + # Reset + max.Reset() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + def test_signal_max_absolute_value_constant_values(self): + # Constant values, max should match + max = SignalMaxAbsoluteValue() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + max.InsertData(value) + self.assertAlmostEqual(max.Value(), value) + self.assertEqual(max.Count(), i) + + # Reset + max.Reset() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + def test_signal_max_absolute_value_alternating_values(self): + # Values with alternating sign, increasing magnitude + # max should match absolute value every time + max = SignalMaxAbsoluteValue() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + max.InsertData(value * i) + self.assertAlmostEqual(max.Value(), value * i) + self.assertEqual(max.Count(), i*2-1) + + max.InsertData(-value * i) + self.assertAlmostEqual(max.Value(), value * i) + self.assertEqual(max.Count(), i*2) + + # Reset + max.Reset() + self.assertAlmostEqual(max.Value(), 0.0) + self.assertEqual(max.Count(), 0) + + def test_signal_variance_constructor(self): + var = SignalVariance() + self.assertAlmostEqual(var.Value(), 0.0) + self.assertEqual(var.Count(), 0) + self.assertEqual(var.ShortName(), "var") + + # Reset + var.Reset() + self.assertAlmostEqual(var.Value(), 0.0) + self.assertEqual(var.Count(), 0) + + def test_signal_variance_one_value(self): + # Add one value, expect 0.0 variance + values = {0, 1.0, 10.0, -100.0} + for value in values: + var = SignalVariance() + var.InsertData(value) + self.assertEqual(var.Count(), 1) + self.assertAlmostEqual(0.0, var.Value()) + + # Reset + var.Reset() + self.assertAlmostEqual(0.0, var.Value()) + self.assertEqual(var.Count(), 0) + + def test_signal_variance_constant_values(self): + # Constant values, expect 0.0 variance + var = SignalVariance() + value = 3.14159 + + # Loop two times to verify Reset + for j in range(2): + for i in range(1, 11): + var.InsertData(value) + self.assertAlmostEqual(0.0, var.Value()) + self.assertEqual(var.Count(), i) + + # Reset + var.Reset() + self.assertAlmostEqual(var.Value(), 0.0) + self.assertEqual(var.Count(), 0) + + def test_signal_variance_random_values(self): + # Random normally distributed values + # The sample variance has the following variance: + # 2 variance^2 / (count - 1) + # en.wikipedia.org/wiki/Variance#Distribution_of_the_sample_variance + # We will use 5 sigma (4e-5 chance of failure) + var = SignalVariance() + stdDev = 3.14159 + count = 10000 + sigma = 5.0 + for i in range(count): + var.InsertData(Rand.DblNormal(0.0, stdDev)) + + variance = stdDev*stdDev + sampleVariance2 = 2 * variance*variance / (count - 1) + self.assertAlmostEqual(var.Value(), variance, + delta=sigma*math.sqrt(sampleVariance2)) + + # Reset + var.Reset() + self.assertAlmostEqual(var.Value(), 0.0) + self.assertEqual(var.Count(), 0) + + def test_signal_stats_constructor(self): + # Constructor + stats = SignalStats() + self.assertTrue(stats.Map().empty()) + self.assertEqual(stats.Count(), 0) + + stats2 = SignalStats(stats) + self.assertEqual(stats.Count(), stats2.Count()) + + # Reset + stats.Reset() + self.assertTrue(stats.Map().empty()) + self.assertEqual(stats.Count(), 0) + + def test_01_signal_stats_intern_statistic(self): + # InsertStatistic + stats = SignalStats() + self.assertTrue(stats.Map().empty()) + + self.assertTrue(stats.InsertStatistic("max")) + self.assertFalse(stats.InsertStatistic("max")) + self.assertFalse(stats.Map().empty()) + + self.assertTrue(stats.InsertStatistic("maxAbs")) + self.assertFalse(stats.InsertStatistic("maxAbs")) + self.assertFalse(stats.Map().empty()) + + self.assertTrue(stats.InsertStatistic("mean")) + self.assertFalse(stats.InsertStatistic("mean")) + self.assertFalse(stats.Map().empty()) + + self.assertTrue(stats.InsertStatistic("min")) + self.assertFalse(stats.InsertStatistic("min")) + self.assertFalse(stats.Map().empty()) + + self.assertTrue(stats.InsertStatistic("rms")) + self.assertFalse(stats.InsertStatistic("rms")) + self.assertFalse(stats.Map().empty()) + + self.assertTrue(stats.InsertStatistic("var")) + self.assertFalse(stats.InsertStatistic("var")) + self.assertFalse(stats.Map().empty()) + + self.assertFalse(stats.InsertStatistic("FakeStatistic")) + + # Map with no data + map = stats.Map() + self.assertFalse(map.empty()) + self.assertEqual(map.size(), 6) + self.assertEqual(map.count("max"), 1) + self.assertEqual(map.count("maxAbs"), 1) + self.assertEqual(map.count("mean"), 1) + self.assertEqual(map.count("min"), 1) + self.assertEqual(map.count("rms"), 1) + self.assertEqual(map.count("var"), 1) + self.assertEqual(map.count("FakeStatistic"), 0) + + stats2 = SignalStats(stats) + map2 = stats2.Map() + self.assertFalse(map2.empty()) + self.assertEqual(map.size(), map2.size()) + self.assertEqual(map.count("max"), map2.count("max")) + self.assertEqual(map.count("maxAbs"), map2.count("maxAbs")) + self.assertEqual(map.count("mean"), map2.count("mean")) + self.assertEqual(map.count("min"), map2.count("min")) + self.assertEqual(map.count("rms"), map2.count("rms")) + self.assertEqual(map.count("var"), map2.count("var")) + self.assertEqual(map.count("FakeStatistic"), + map2.count("FakeStatistic")) + + def test_02_signal_stats_intern_statistic(self): + # InsertStatistics + stats = SignalStats() + self.assertFalse(stats.InsertStatistics("")) + self.assertTrue(stats.Map().empty()) + + self.assertTrue(stats.InsertStatistics("maxAbs,rms")) + self.assertEqual(stats.Map().size(), 2) + self.assertFalse(stats.InsertStatistics("maxAbs,rms")) + self.assertFalse(stats.InsertStatistics("maxAbs")) + self.assertFalse(stats.InsertStatistics("rms")) + self.assertEqual(stats.Map().size(), 2) + + self.assertFalse(stats.InsertStatistics("mean,FakeStatistic")) + self.assertEqual(stats.Map().size(), 3) + + self.assertFalse(stats.InsertStatistics("var,FakeStatistic")) + self.assertEqual(stats.Map().size(), 4) + + self.assertFalse(stats.InsertStatistics("max,FakeStatistic")) + self.assertEqual(stats.Map().size(), 5) + + self.assertFalse(stats.InsertStatistics("min,FakeStatistic")) + self.assertEqual(stats.Map().size(), 6) + + self.assertFalse(stats.InsertStatistics("FakeStatistic")) + self.assertEqual(stats.Map().size(), 6) + + # Map with no data + map = stats.Map() + self.assertFalse(map.empty()) + self.assertEqual(map.size(), 6) + self.assertEqual(map.count("max"), 1) + self.assertEqual(map.count("maxAbs"), 1) + self.assertEqual(map.count("mean"), 1) + self.assertEqual(map.count("min"), 1) + self.assertEqual(map.count("rms"), 1) + self.assertEqual(map.count("var"), 1) + self.assertEqual(map.count("FakeStatistic"), 0) + + def test_signal_stats_alternating_values(self): + # Add some statistics + stats = SignalStats() + self.assertTrue(stats.InsertStatistics("max,maxAbs,mean,min,rms")) + self.assertEqual(stats.Map().size(), 5) + + # No data yet + self.assertEqual(stats.Count(), 0) + + # Insert data with alternating signs + value = 3.14159 + stats.InsertData(value) + stats.InsertData(-value) + self.assertEqual(stats.Count(), 2) + + map = stats.Map() + self.assertAlmostEqual(map["max"], value) + self.assertAlmostEqual(map["maxAbs"], value) + self.assertAlmostEqual(map["min"], -value) + self.assertAlmostEqual(map["rms"], value) + self.assertAlmostEqual(map["mean"], 0.0) + + # test operator= + copy = SignalStats(stats) + self.assertEqual(copy.Count(), 2) + map = stats.Map() + self.assertEqual(map.size(), 5) + self.assertAlmostEqual(map["max"], value) + self.assertAlmostEqual(map["maxAbs"], value) + self.assertAlmostEqual(map["min"], -value) + self.assertAlmostEqual(map["rms"], value) + self.assertAlmostEqual(map["mean"], 0.0) + + stats.Reset() + self.assertEqual(stats.Map().size(), 5) + self.assertEqual(stats.Count(), 0) + map = stats.Map() + self.assertAlmostEqual(map["max"], 0.0) + self.assertAlmostEqual(map["maxAbs"], 0.0) + self.assertAlmostEqual(map["min"], 0.0) + self.assertAlmostEqual(map["rms"], 0.0) + self.assertAlmostEqual(map["mean"], 0.0) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/Temperature.i b/src/Temperature.i new file mode 100644 index 000000000..bf1926245 --- /dev/null +++ b/src/Temperature.i @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +%module temperature +%{ +#include +%} + +%include "std_string.i" + +namespace ignition +{ + namespace math + { + class Temperature + { + public: Temperature(); + public: Temperature(const double _temp); + public: Temperature(const Temperature &_temp); + public: virtual ~Temperature(); + public: static double KelvinToCelsius(const double _temp); + public: static double KelvinToFahrenheit(const double _temp); + public: static double CelsiusToFahrenheit(const double _temp); + public: static double CelsiusToKelvin(const double _temp); + public: static double FahrenheitToCelsius(const double _temp); + public: static double FahrenheitToKelvin(const double _temp); + public: void SetKelvin(const double _temp); + public: void SetCelsius(const double _temp); + public: void SetFahrenheit(const double _temp); + public: double Kelvin() const; + public: double Celsius() const; + public: double Fahrenheit() const; + public: double operator()() const; + public: Temperature operator+(const double _temp); + public: Temperature operator+(const Temperature &_temp); + public: const Temperature &operator+=(const double _temp); + public: const Temperature &operator+=(const Temperature &_temp); + public: Temperature operator-(const double _temp); + public: Temperature operator-(const Temperature &_temp); + public: const Temperature &operator-=(const double _temp); + public: const Temperature &operator-=(const Temperature &_temp); + public: Temperature operator*(const double _temp); + public: Temperature operator*(const Temperature &_temp); + public: const Temperature &operator*=(const double _temp); + public: const Temperature &operator*=(const Temperature &_temp); + public: Temperature operator/(const double _temp); + public: Temperature operator/(const Temperature &_temp); + public: const Temperature &operator/=(const double _temp); + public: const Temperature &operator/=(const Temperature &_temp); + public: bool operator==(const Temperature &_temp) const; + public: bool operator==(const double _temp) const; + public: bool operator!=(const Temperature &_temp) const; + public: bool operator!=(const double _temp) const; + public: bool operator<(const Temperature &_temp) const; + public: bool operator<(const double _temp) const; + public: bool operator<=(const Temperature &_temp) const; + public: bool operator<=(const double _temp) const; + public: bool operator>(const Temperature &_temp) const; + public: bool operator>(const double _temp) const; + public: bool operator>=(const Temperature &_temp) const; + public: bool operator>=(const double _temp) const; + }; + + %extend Temperature + { + std::string __str__() const { + std::ostringstream out; + out << *$self; + return out.str(); + } + } + } +} diff --git a/src/Temperature_TEST.py b/src/Temperature_TEST.py new file mode 100644 index 000000000..f80233559 --- /dev/null +++ b/src/Temperature_TEST.py @@ -0,0 +1,130 @@ +# Copyright (C) 2021 Open Source Robotics Foundation + +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from ignition.math import Temperature + + +class TestTemperature(unittest.TestCase): + + def test_temperatur_constructor(self): + temp = Temperature() + self.assertAlmostEqual(temp.Kelvin(), 0.0, 1e-6) + + temp2 = Temperature(1.1) + self.assertAlmostEqual(temp2.Kelvin(), 1.1, 1e-6) + + temp3 = Temperature(temp2) + self.assertAlmostEqual(temp3.Kelvin(), 1.1, delta=1e-6) + self.assertAlmostEqual(temp3.Celsius(), -272.05, delta=1e-6) + + self.assertTrue(temp2 == temp3) + self.assertTrue(temp2 == 1.1) + self.assertTrue(temp2 != temp) + self.assertTrue(temp2 != 1.2) + + self.assertTrue(temp < temp2) + self.assertTrue(temp < 10.0) + self.assertTrue(temp <= temp2) + self.assertTrue(temp <= 0.0) + self.assertTrue(temp <= 0.1) + + self.assertFalse(temp > temp2) + self.assertFalse(temp > 80.0) + self.assertFalse(temp >= temp2) + self.assertFalse(temp >= 0.1) + self.assertTrue(temp >= 0.0) + + def test_temperatur_conversions(self): + self.assertAlmostEqual(Temperature.KelvinToCelsius(0), -273.15, + delta=1e-6) + self.assertAlmostEqual(Temperature.KelvinToFahrenheit(300), 80.33, + delta=1e-6) + self.assertAlmostEqual(Temperature.CelsiusToFahrenheit(20.0), 68.0, + delta=1e-6) + self.assertAlmostEqual(Temperature.CelsiusToKelvin(10.0), 283.15, + delta=1e-6) + self.assertAlmostEqual(Temperature.FahrenheitToCelsius(-40.0), + Temperature.CelsiusToFahrenheit(-40.0), 1e-6) + self.assertAlmostEqual(Temperature.FahrenheitToKelvin(60.0), 288.7055, + delta=1e-3) + + def test_temperatur_mutators_accessors(self): + temp = Temperature() + self.assertAlmostEqual(temp.Kelvin(), 0.0, delta=1e-6) + + temp.SetKelvin(10) + self.assertAlmostEqual(temp.Kelvin(), 10.0, delta=1e-6) + + temp.SetCelsius(20) + self.assertAlmostEqual(temp(), 293.15, delta=1e-6) + + temp.SetFahrenheit(30) + self.assertAlmostEqual(temp.Fahrenheit(), 30.0, delta=1e-6) + self.assertAlmostEqual(temp(), 272.0388889, delta=1e-6) + + def test_temperatur_operators(self): + temp = Temperature(20) + self.assertAlmostEqual(temp(), 20, delta=1e-6) + + temp = Temperature(30) + + self.assertAlmostEqual(temp(), 30, delta=1e-6) + + temp2 = Temperature(temp) + self.assertTrue(temp == temp2) + + self.assertAlmostEqual((temp + temp2).Kelvin(), 60, delta=1e-6) + self.assertAlmostEqual((temp + 40).Kelvin(), 70, delta=1e-6) + + self.assertAlmostEqual((temp - temp2).Kelvin(), 0, delta=1e-6) + self.assertAlmostEqual((temp - 20).Kelvin(), 10.0, delta=1e-6) + + self.assertAlmostEqual((temp * temp2).Kelvin(), 900, delta=1e-6) + self.assertAlmostEqual((temp * 2).Kelvin(), 60.0, delta=1e-6) + + self.assertAlmostEqual((temp / temp2).Kelvin(), 1.0, delta=1e-6) + self.assertAlmostEqual((temp / 2).Kelvin(), 15.0, delta=1e-6) + + temp += temp2 + self.assertAlmostEqual(temp.Kelvin(), 60.0, delta=1e-6) + temp -= temp2 + self.assertAlmostEqual(temp.Kelvin(), 30.0, delta=1e-6) + + temp += 5.0 + self.assertAlmostEqual(temp.Kelvin(), 35.0, delta=1e-6) + temp -= 5.0 + self.assertAlmostEqual(temp.Kelvin(), 30.0, delta=1e-6) + + temp *= temp2 + self.assertAlmostEqual(temp.Kelvin(), 900, delta=1e-6) + temp /= temp2 + self.assertAlmostEqual(temp.Kelvin(), 30, delta=1e-6) + + temp *= 4.0 + self.assertAlmostEqual(temp.Kelvin(), 120, delta=1e-6) + temp /= 4.0 + self.assertAlmostEqual(temp.Kelvin(), 30, delta=1e-6) + + temp3 = Temperature(temp) + self.assertTrue(temp3 == temp) + self.assertTrue(temp3 == temp2) + + def test_serialization(self): + temp = Temperature(55.45) + self.assertEqual(str(temp), "55.45") + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/python.i b/src/python/python.i index 614c220bd..cad4e03ac 100644 --- a/src/python/python.i +++ b/src/python/python.i @@ -5,3 +5,7 @@ %include Vector2.i %include Vector3.i %include Vector4.i +%include Temperature.i +%include Line2.i +%include Line3.i +%include SignalStats.i From 3fefff83c02cb06836d224453ecfb05c0228ebc2 Mon Sep 17 00:00:00 2001 From: Marcos Wagner Date: Fri, 13 Aug 2021 14:50:47 -0300 Subject: [PATCH 2/5] Adding Line3 class with its test Signed-off-by: Marcos Wagner --- src/Line3.i | 91 +++++++++++++++++ src/Line3_TEST.py | 249 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 src/Line3.i create mode 100644 src/Line3_TEST.py diff --git a/src/Line3.i b/src/Line3.i new file mode 100644 index 000000000..6447f6f1c --- /dev/null +++ b/src/Line3.i @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +%module line3 +%{ +#include +#include +#include +%} + +%include "std_string.i" + +namespace ignition +{ + namespace math + { + template + class Line3 + { + public: Line3() = default; + public: Line3(const Line3 &_line); + public: Line3(const math::Vector3 &_ptA, const math::Vector3 &_ptB); + public: Line3(const double _x1, const double _y1, + const double _x2, const double _y2); + public: Line3(const double _x1, const double _y1, + const double _z1, const double _x2, + const double _y2, const double _z2); + public: void Set(const math::Vector3 &_ptA, + const math::Vector3 &_ptB); + public: void SetA(const math::Vector3 &_ptA); + public: void SetB(const math::Vector3 &_ptB); + public: void Set(const double _x1, const double _y1, + const double _x2, const double _y2, + const double _z = 0); + public: void Set(const double _x1, const double _y1, + const double _z1, const double _x2, + const double _y2, const double _z2); + public: math::Vector3 Direction() const; + public: T Length() const; + public: bool Distance(const Line3 &_line, Line3 &_result, + const double _epsilon = 1e-6) const; + public: bool Intersect(const Line3 &_line, + double _epsilon = 1e-6) const; + public: bool Coplanar(const Line3 &_line, + const double _epsilon = 1e-6) const; + public: bool Parallel(const Line3 &_line, + const double _epsilon = 1e-6) const; + public: bool Intersect(const Line3 &_line, math::Vector3 &_pt, + double _epsilon = 1e-6) const; + public: bool Within(const math::Vector3 &_pt, + double _epsilon = 1e-6) const; + public: bool operator==(const Line3 &_line) const; + public: bool operator!=(const Line3 &_line) const; + }; + + %extend Line3 + { + ignition::math::Vector3 __getitem__(const unsigned int i) const + { + return (*$self)[i]; + } + } + + %extend Line3 + { + std::string __str__() const { + std::ostringstream out; + out << *$self; + return out.str(); + } + } + + %template(Line3i) Line3; + %template(Line3d) Line3; + %template(Line3f) Line3; + } +} diff --git a/src/Line3_TEST.py b/src/Line3_TEST.py new file mode 100644 index 000000000..0fa2109f6 --- /dev/null +++ b/src/Line3_TEST.py @@ -0,0 +1,249 @@ +# Copyright (C) 2021 Open Source Robotics Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import math +from ignition.math import Line3d +from ignition.math import Vector3d + + +class TestLine3d(unittest.TestCase): + + def test_construction(self): + lineA = Line3d(0, 0, 10, 10) + self.assertAlmostEqual(lineA[0].X(), 0.0) + self.assertAlmostEqual(lineA[0].Y(), 0.0) + self.assertAlmostEqual(lineA[0].Z(), 0.0) + self.assertAlmostEqual(lineA[1].X(), 10.0) + self.assertAlmostEqual(lineA[1].Y(), 10.0) + self.assertAlmostEqual(lineA[1].Z(), 0.0) + lineB = Line3d(Vector3d(1, 2, 3), Vector3d(4, 5, 6)) + self.assertAlmostEqual(lineB[0].X(), 1.0) + self.assertAlmostEqual(lineB[0].Y(), 2.0) + self.assertAlmostEqual(lineB[0].Z(), 3.0) + self.assertAlmostEqual(lineB[1].X(), 4.0) + self.assertAlmostEqual(lineB[1].Y(), 5.0) + self.assertAlmostEqual(lineB[1].Z(), 6.0) + + lineC = Line3d(0, 0, 5, 10, 10, 6) + self.assertAlmostEqual(lineC[0].X(), 0.0) + self.assertAlmostEqual(lineC[0].Y(), 0.0) + self.assertAlmostEqual(lineC[0].Z(), 5.0) + self.assertAlmostEqual(lineC[1].X(), 10.0) + self.assertAlmostEqual(lineC[1].Y(), 10.0) + self.assertAlmostEqual(lineC[1].Z(), 6.0) + + self.assertAlmostEqual(lineB[2].X(), lineB[1].X()) + + def test_set(self): + lineA = Line3d() + lineA.Set(1, 1, 2, 2) + self.assertAlmostEqual(lineA[0].X(), 1.0) + self.assertAlmostEqual(lineA[0].Y(), 1.0) + self.assertAlmostEqual(lineA[0].Z(), 0.0) + self.assertAlmostEqual(lineA[1].X(), 2.0) + self.assertAlmostEqual(lineA[1].Y(), 2.0) + self.assertAlmostEqual(lineA[1].Z(), 0.0) + + lineA.Set(10, 11, 12, 13, 14, 15) + self.assertAlmostEqual(lineA[0].X(), 10.0) + self.assertAlmostEqual(lineA[0].Y(), 11.0) + self.assertAlmostEqual(lineA[0].Z(), 12.0) + self.assertAlmostEqual(lineA[1].X(), 13.0) + self.assertAlmostEqual(lineA[1].Y(), 14.0) + self.assertAlmostEqual(lineA[1].Z(), 15.0) + + lineA.SetA(Vector3d(0, -1, -2)) + self.assertAlmostEqual(lineA[0].X(), 0.0) + self.assertAlmostEqual(lineA[0].Y(), -1.0) + self.assertAlmostEqual(lineA[0].Z(), -2.0) + self.assertAlmostEqual(lineA[1].X(), 13.0) + self.assertAlmostEqual(lineA[1].Y(), 14.0) + self.assertAlmostEqual(lineA[1].Z(), 15.0) + + lineA.SetB(Vector3d(5, 6, 7)) + self.assertAlmostEqual(lineA[0].X(), 0.0) + self.assertAlmostEqual(lineA[0].Y(), -1.0) + self.assertAlmostEqual(lineA[0].Z(), -2.0) + self.assertAlmostEqual(lineA[1].X(), 5.0) + self.assertAlmostEqual(lineA[1].Y(), 6.0) + self.assertAlmostEqual(lineA[1].Z(), 7.0) + + def test_length(self): + lineA = Line3d(0, 0, 0, 10, 10, 10) + self.assertAlmostEqual(lineA.Length(), math.sqrt(300), delta=1e-10) + + def test_equality(self): + lineA = Line3d(1, 1, 1, 2, 1, 2) + lineB = Line3d(1, 2, 3, 2, 2, 4) + + self.assertTrue(lineA != lineB) + self.assertTrue(lineA == lineA) + + lineB.Set(1, 1, 1, 2, 1.1, 2) + self.assertFalse(lineA == lineB) + + lineB.Set(1, 1, 1, 2.1, 1, 2) + self.assertFalse(lineA == lineB) + + lineB.Set(1, 1, 1.1, 2, 1, 2) + self.assertFalse(lineA == lineB) + + lineB.Set(1.1, 1, 1, 2, 1, 2) + self.assertFalse(lineA == lineB) + + def test_serialization(self): + line = Line3d(0, 1, 4, 2, 3, 7) + self.assertEqual(str(line), "0 1 4 2 3 7") + + def test_copy_constructor(self): + lineA = Line3d(0, 1, 4, 2, 3, 7) + lineB = Line3d(lineA) + + self.assertEqual(lineA, lineB) + + def test_direction(self): + lineA = Line3d(1, 1, 1, 0, 0, 0) + lineB = Line3d(2, 2, 2, 0, 0, 0) + lineC = Line3d(0, 0, 0, 1, 1, 1) + self.assertTrue(lineA.Direction() == (lineA[1] - lineA[0]).Normalize()) + self.assertTrue(lineA.Direction() == lineB.Direction()) + self.assertFalse(lineA.Direction() == lineC.Direction()) + + lineA.Set(1, 1, 2, 1, 1, 10) + self.assertTrue(lineA.Direction() == Vector3d.UnitZ) + + lineA.Set(1, 5, 1, 1, 1, 1) + self.assertTrue(lineA.Direction() == -Vector3d.UnitY) + + lineA.Set(1, 1, 1, 7, 1, 1) + self.assertTrue(lineA.Direction() == Vector3d.UnitX) + + def test_within(self): + line = Line3d(0, 0, 0, 1, 1, 1) + self.assertTrue(line.Within(Vector3d(0, 0, 0))) + self.assertTrue(line.Within(Vector3d(1, 1, 1))) + self.assertTrue(line.Within(Vector3d(0.5, 0.5, 0.5))) + + self.assertFalse(line.Within(Vector3d(-0.5, 0.5, 0.5))) + self.assertFalse(line.Within(Vector3d(0.5, -0.5, 0.5))) + self.assertFalse(line.Within(Vector3d(0.5, 0.5, -0.5))) + + def test_distance(self): + line = Line3d(0, 0, 0, 0, 1, 0) + result = Line3d() + + self.assertTrue(line.Distance(Line3d(1, 0.5, 0, -1, 0.5, 0), result)) + self.assertAlmostEqual(result.Length(), 0) + self.assertEqual(result, Line3d(0, 0.5, 0, 0, 0.5, 0)) + + self.assertTrue(line.Distance(Line3d(1, 0, 0, -1, 0, 0), result)) + self.assertAlmostEqual(result.Length(), 0) + self.assertEqual(result, Line3d(0, 0, 0, 0, 0, 0)) + + self.assertTrue(line.Distance(Line3d(1, 1.1, 0, -1, 1.1, 0), result)) + self.assertAlmostEqual(result.Length(), 0.1, delta=1e-4) + self.assertEqual(result, Line3d(0, 1, 0, 0, 1.1, 0)) + + self.assertTrue(line.Distance(Line3d(1, 0.5, 0.4, -1, 0.5, 0.4), + result)) + self.assertAlmostEqual(result.Length(), 0.4, delta=1e-4) + self.assertEqual(result, Line3d(0, 0.5, 0, 0, 0.5, 0.4)) + + self.assertTrue(line.Distance(Line3d(0, 0.5, 1, 1, 0.5, 0), + result)) + self.assertAlmostEqual(result.Length(), math.sin(math.pi / 4), + delta=1e-4) + self.assertEqual(result, Line3d(0, 0.5, 0, 0.5, 0.5, 0.5)) + + # Expect true when lines are parallel + self.assertTrue(line.Distance(Line3d(2, 0, 0, 2, 1, 0), result)) + self.assertEqual(result[0], line[0]) + self.assertEqual(result[1], Vector3d(2, 0, 0)) + + self.assertTrue(line.Distance(Line3d(2, 1, 0, 2, 0, 0), result)) + self.assertEqual(result[0], line[0]) + self.assertEqual(result[1], Vector3d(2, 0, 0)) + + self.assertTrue(line.Distance(Line3d(1, 1, 0, 1, 2, 0), result)) + self.assertEqual(result[0], line[1]) + self.assertEqual(result[1], Vector3d(1, 1, 0)) + + self.assertTrue(line.Distance(Line3d(1, 2, 0, 1, 1, 0), result)) + self.assertEqual(result[0], line[1]) + self.assertEqual(result[1], Vector3d(1, 1, 0)) + + # Expect false when the passed in line is a point + self.assertFalse(line.Distance(Line3d(2, 0, 0, 2, 0, 0), result)) + + # Expect false when the first line is a point. + line.Set(0, 0, 0, 0, 0, 0) + self.assertFalse(line.Distance(Line3d(2, 0, 0, 2, 1, 0), result)) + + def test_interesct(self): + line = Line3d(0, 0, 0, 0, 1, 0) + pt = Vector3d() + + self.assertTrue(line.Intersect(Line3d(1, 0.5, 0, -1, 0.5, 0))) + self.assertTrue(line.Intersect(Line3d(1, 0.5, 0, -1, 0.5, 0), pt)) + self.assertEqual(pt, Vector3d(0, 0.5, 0)) + + self.assertTrue(line.Intersect(Line3d(1, 0, 0, -1, 0, 0))) + self.assertTrue(line.Intersect(Line3d(1, 0, 0, -1, 0, 0), pt)) + self.assertEqual(pt, Vector3d(0, 0, 0)) + + self.assertTrue(line.Intersect(Line3d(1, 1, 0, -1, 1, 0))) + self.assertTrue(line.Intersect(Line3d(1, 1, 0, -1, 1, 0), pt)) + self.assertEqual(pt, Vector3d(0, 1, 0)) + + self.assertTrue(line.Intersect(Line3d(0, 0.5, -1, 0, 0.5, 1))) + self.assertTrue(line.Intersect(Line3d(0, 0.5, -1, 0, 0.5, 1), pt)) + self.assertEqual(pt, Vector3d(0, 0.5, 0)) + + self.assertTrue(line.Intersect(Line3d(-1, 0.5, -1, 1, 0.5, 1))) + self.assertTrue(line.Intersect(Line3d(-1, 0.5, -1, 1, 0.5, 1), pt)) + self.assertEqual(pt, Vector3d(0, 0.5, 0)) + + self.assertFalse(line.Intersect(Line3d(1, 1.1, 0, -1, 1.1, 0))) + self.assertFalse(line.Intersect(Line3d(1, -0.1, 0, -1, -0.1, 0))) + + self.assertFalse(line.Intersect(Line3d(0.1, 0.1, 0, 0.6, 0.6, 0))) + self.assertFalse(line.Intersect(Line3d(-0.1, 0, 0, -0.1, 1, 0))) + + self.assertTrue(line.Intersect(Line3d(0, -1, 0, 0, 0.1, 0))) + self.assertTrue(line.Intersect(Line3d(0, 1, 0, 0, 1.1, 0))) + + def test_parallel(self): + line = Line3d(0, 0, 0, 0, 1, 0) + self.assertTrue(line.Parallel(Line3d(1, 0, 0, 1, 1, 0))) + self.assertTrue(line.Parallel(Line3d(1, 1, 0, 1, 0, 0))) + self.assertTrue(line.Parallel(Line3d(0, 0, 0, 0, 10, 0))) + self.assertTrue(line.Parallel(Line3d(-100, 100, 20, -100, 200, 20))) + + self.assertFalse(line.Parallel(Line3d(1, 0, 0, 1, 1, 1))) + self.assertFalse(line.Parallel(Line3d(1, 0, 0, 2, 0, 0))) + self.assertFalse(line.Parallel(Line3d(1, 0, 1, 2, 0, 1))) + + def test_coplanar(self): + line = Line3d(0, 0, 0, 0, 1, 0) + self.assertTrue(line.Coplanar(Line3d(1, 0, 0, 1, 1, 0))) + self.assertTrue(line.Coplanar(Line3d(0, 0, 0, 0, 10, 0))) + self.assertTrue(line.Coplanar(Line3d(-100, 100, 20, -100, 200, 20))) + + self.assertFalse(line.Coplanar(Line3d(1, 0, 0, 1, 1, 1))) + self.assertFalse(line.Coplanar(Line3d(1, 0, 1, 2, 0, 0))) + + +if __name__ == '__main__': + unittest.main() From 31be8f82e8fef4205f96e5972e2584e57f28956b Mon Sep 17 00:00:00 2001 From: Marcos Wagner Date: Tue, 24 Aug 2021 15:51:15 -0300 Subject: [PATCH 3/5] Adding %rename tag to interface files to match PEP8 Signed-off-by: Marcos Wagner --- src/Line2_TEST.py | 215 ----------- src/Line3.i | 91 ----- src/Line3_TEST.py | 249 ------------- src/SignalStats.i | 100 ----- src/SignalStats_TEST.py | 528 --------------------------- src/python/CMakeLists.txt | 10 +- src/{ => python}/Line2.i | 1 + src/python/Line2_TEST.py | 215 +++++++++++ src/python/Line3.i | 92 +++++ src/python/Line3_TEST.py | 249 +++++++++++++ src/python/SignalStats.i | 107 ++++++ src/python/SignalStats_TEST.py | 527 ++++++++++++++++++++++++++ src/{ => python}/Temperature.i | 3 +- src/{ => python}/Temperature_TEST.py | 68 ++-- src/python/python.i | 2 +- 15 files changed, 1235 insertions(+), 1222 deletions(-) delete mode 100644 src/Line2_TEST.py delete mode 100644 src/Line3.i delete mode 100644 src/Line3_TEST.py delete mode 100644 src/SignalStats.i delete mode 100644 src/SignalStats_TEST.py rename src/{ => python}/Line2.i (97%) create mode 100644 src/python/Line2_TEST.py create mode 100644 src/python/Line3.i create mode 100644 src/python/Line3_TEST.py create mode 100644 src/python/SignalStats.i create mode 100644 src/python/SignalStats_TEST.py rename src/{ => python}/Temperature.i (97%) rename src/{ => python}/Temperature_TEST.py (55%) diff --git a/src/Line2_TEST.py b/src/Line2_TEST.py deleted file mode 100644 index 13351b1a1..000000000 --- a/src/Line2_TEST.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright (C) 2021 Open Source Robotics Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License") -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -import math -from ignition.math import Line2d -from ignition.math import Vector2d - - -class TestLine2d(unittest.TestCase): - - def test_construction(self): - lineA = Line2d(0, 0, 10, 10) - self.assertAlmostEqual(lineA[0].X(), 0.0) - self.assertAlmostEqual(lineA[0].Y(), 0.0) - self.assertAlmostEqual(lineA[1].X(), 10.0) - self.assertAlmostEqual(lineA[1].Y(), 10.0) - - lineB = Line2d(Vector2d(1, 2), Vector2d(3, 4)) - self.assertAlmostEqual(lineB[0].X(), 1.0) - self.assertAlmostEqual(lineB[0].Y(), 2.0) - self.assertAlmostEqual(lineB[1].X(), 3.0) - self.assertAlmostEqual(lineB[1].Y(), 4.0) - - self.assertAlmostEqual(lineB[2].X(), lineB[1].X()) - - def test_length(self): - lineA = Line2d(0, 0, 10, 10) - self.assertAlmostEqual(lineA.Length(), math.sqrt(200), delta=1e-10) - - def test_slope(self): - line = Line2d(0, 0, 10, 10) - self.assertAlmostEqual(line.Slope(), 1.0, delta=1e-10) - - line = Line2d(0, 0, 0, 10) - self.assertTrue(math.isnan(line.Slope())) - - line = Line2d(-10, 0, 100, 0) - self.assertAlmostEqual(line.Slope(), 0.0) - - def test_parallel_line(self): - # Line is always parallel with itself - line = Line2d(0, 0, 10, 0) - self.assertTrue(line.Parallel(line, 1e-10)) - - # Degenerate line segment - # Still expect Line is parallel with itself - line = Line2d(0, 0, 0, 0) - self.assertTrue(line.Parallel(line, 1e-10)) - - lineA = Line2d(0, 0, 10, 0) - lineB = Line2d(0, 0, 10, 0) - self.assertTrue(lineA.Parallel(lineB, 1e-10)) - - lineB.Set(0, 0, 0, 10) - self.assertFalse(lineA.Parallel(lineB)) - - lineB.Set(0, 10, 10, 10) - self.assertTrue(lineA.Parallel(lineB)) - - lineB.Set(0, 10, 10, 10.00001) - self.assertFalse(lineA.Parallel(lineB, 1e-10)) - self.assertFalse(lineA.Parallel(lineB)) - self.assertTrue(lineA.Parallel(lineB, 1e-3)) - - def test_collinear_line(self): - # Line is always collinear with itself - line = Line2d(0, 0, 10, 0) - self.assertTrue(line.Collinear(line, 1e-10)) - - lineA = Line2d(0, 0, 10, 0) - lineB = Line2d(0, 0, 10, 0) - self.assertTrue(lineA.Collinear(lineB, 1e-10)) - - lineB.Set(0, 10, 10, 10) - self.assertFalse(lineA.Collinear(lineB)) - - lineB.Set(9, 0, 10, 0.00001) - self.assertFalse(lineA.Collinear(lineB, 1e-10)) - self.assertFalse(lineA.Collinear(lineB)) - self.assertTrue(lineA.Collinear(lineB, 1e-3)) - - def test_collinear_point(self): - lineA = Line2d(0, 0, 10, 0) - pt = Vector2d(0, 0) - self.assertTrue(lineA.Collinear(pt)) - - ptLine = Line2d(pt, pt) - self.assertTrue(lineA.Collinear(ptLine)) - - pt.Set(1000, 0) - self.assertTrue(lineA.Collinear(pt, 1e-10)) - - ptLine = Line2d(pt, pt) - self.assertTrue(lineA.Parallel(ptLine)) - self.assertFalse(lineA.Intersect(ptLine)) - self.assertFalse(lineA.Collinear(ptLine, 1e-10)) - - pt.Set(10, 0) - ptLine.Set(pt, pt) - self.assertTrue(lineA.Collinear(ptLine, 1e-10)) - - pt.Set(0, 0.00001) - self.assertFalse(lineA.Collinear(pt)) - self.assertTrue(lineA.Collinear(pt, 1e-3)) - - ptLine = Line2d(pt, pt) - self.assertFalse(lineA.Collinear(ptLine)) - self.assertTrue(lineA.Parallel(ptLine)) - self.assertFalse(lineA.Intersect(ptLine)) - self.assertTrue(lineA.Intersect(ptLine, 1e-2)) - self.assertTrue(lineA.Collinear(ptLine, 1e-3)) - - pt.Set(0, -0.00001) - self.assertFalse(lineA.Collinear(pt)) - self.assertTrue(lineA.Collinear(pt, 1e-3)) - - ptLine = Line2d(pt, pt) - self.assertFalse(lineA.Collinear(ptLine)) - self.assertTrue(lineA.Collinear(ptLine, 1e-4)) - - def test_intersect(self): - pt = Vector2d() - - # Parallel horizontal lines - lineA = Line2d(1, 1, 2, 1) - lineB = Line2d(1, 2, 2, 2) - self.assertFalse(lineA.Intersect(lineB, pt)) - - # Parallel vertical lines - lineA.Set(1, 1, 1, 10) - lineB.Set(2, 1, 2, 10) - self.assertFalse(lineA.Intersect(lineB, pt)) - - # Two lines that form an inverted T with a gap - lineA.Set(1, 1, 1, 10) - lineB.Set(0, 0, 2, 0) - self.assertFalse(lineA.Intersect(lineB, pt)) - - # Two lines that form a T with a gap - lineA.Set(1, 1, 1, 10) - lineB.Set(0, 10.1, 2, 10.1) - self.assertFalse(lineA.Intersect(lineB, pt)) - - # Two lines that form an inverted T with a gap - lineA.Set(0, -10, 0, 10) - lineB.Set(1, 0, 10, 0) - self.assertFalse(lineA.Intersect(lineB, pt)) - - # Two lines that form a T with a gap - lineA.Set(0, -10, 0, 10) - lineB.Set(-1, 0, -10, 0) - self.assertFalse(lineA.Intersect(lineB, pt)) - - # Two collinear lines, one starts where the other stopped - lineA.Set(1, 1, 1, 10) - lineB.Set(1, 10, 1, 11) - self.assertTrue(lineA.Intersect(lineB, pt)) - self.assertEqual(pt, Vector2d(1, 10)) - - # Two collinear lines, one overlaps the other - lineA.Set(0, 0, 0, 10) - lineB.Set(0, 9, 0, 11) - self.assertTrue(lineA.Intersect(lineB, pt)) - self.assertEqual(pt, Vector2d(0, 9)) - - # Two collinear lines, one overlaps the other - lineA.Set(0, 0, 0, 10) - lineB.Set(0, -10, 0, 1) - self.assertTrue(lineA.Intersect(lineB, pt)) - self.assertEqual(pt, Vector2d(0, 1)) - - # Two intersecting lines - lineA.Set(0, 0, 10, 10) - lineB.Set(0, 10, 10, 0) - self.assertTrue(lineA.Intersect(lineB, pt)) - self.assertEqual(pt, Vector2d(5, 5)) - - def test_equality(self): - lineA = Line2d(1, 1, 2, 1) - lineB = Line2d(1, 2, 2, 2) - - self.assertTrue(lineA != lineB) - self.assertTrue(lineA == lineA) - - lineB.Set(1, 1, 2, 1.1) - self.assertFalse(lineA == lineB) - - lineB.Set(1, 1, 2.1, 1) - self.assertFalse(lineA == lineB) - - lineB.Set(1, 1.1, 2, 1) - self.assertFalse(lineA == lineB) - - lineB.Set(1.1, 1, 2, 1) - self.assertFalse(lineA == lineB) - - def test_serialization(self): - line = Line2d(0, 1, 2, 3) - self.assertEqual(str(line), "0 1 2 3") - - -if __name__ == '__main__': - unittest.main() diff --git a/src/Line3.i b/src/Line3.i deleted file mode 100644 index 6447f6f1c..000000000 --- a/src/Line3.i +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2021 Open Source Robotics Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - -%module line3 -%{ -#include -#include -#include -%} - -%include "std_string.i" - -namespace ignition -{ - namespace math - { - template - class Line3 - { - public: Line3() = default; - public: Line3(const Line3 &_line); - public: Line3(const math::Vector3 &_ptA, const math::Vector3 &_ptB); - public: Line3(const double _x1, const double _y1, - const double _x2, const double _y2); - public: Line3(const double _x1, const double _y1, - const double _z1, const double _x2, - const double _y2, const double _z2); - public: void Set(const math::Vector3 &_ptA, - const math::Vector3 &_ptB); - public: void SetA(const math::Vector3 &_ptA); - public: void SetB(const math::Vector3 &_ptB); - public: void Set(const double _x1, const double _y1, - const double _x2, const double _y2, - const double _z = 0); - public: void Set(const double _x1, const double _y1, - const double _z1, const double _x2, - const double _y2, const double _z2); - public: math::Vector3 Direction() const; - public: T Length() const; - public: bool Distance(const Line3 &_line, Line3 &_result, - const double _epsilon = 1e-6) const; - public: bool Intersect(const Line3 &_line, - double _epsilon = 1e-6) const; - public: bool Coplanar(const Line3 &_line, - const double _epsilon = 1e-6) const; - public: bool Parallel(const Line3 &_line, - const double _epsilon = 1e-6) const; - public: bool Intersect(const Line3 &_line, math::Vector3 &_pt, - double _epsilon = 1e-6) const; - public: bool Within(const math::Vector3 &_pt, - double _epsilon = 1e-6) const; - public: bool operator==(const Line3 &_line) const; - public: bool operator!=(const Line3 &_line) const; - }; - - %extend Line3 - { - ignition::math::Vector3 __getitem__(const unsigned int i) const - { - return (*$self)[i]; - } - } - - %extend Line3 - { - std::string __str__() const { - std::ostringstream out; - out << *$self; - return out.str(); - } - } - - %template(Line3i) Line3; - %template(Line3d) Line3; - %template(Line3f) Line3; - } -} diff --git a/src/Line3_TEST.py b/src/Line3_TEST.py deleted file mode 100644 index 0fa2109f6..000000000 --- a/src/Line3_TEST.py +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (C) 2021 Open Source Robotics Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License") -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -import math -from ignition.math import Line3d -from ignition.math import Vector3d - - -class TestLine3d(unittest.TestCase): - - def test_construction(self): - lineA = Line3d(0, 0, 10, 10) - self.assertAlmostEqual(lineA[0].X(), 0.0) - self.assertAlmostEqual(lineA[0].Y(), 0.0) - self.assertAlmostEqual(lineA[0].Z(), 0.0) - self.assertAlmostEqual(lineA[1].X(), 10.0) - self.assertAlmostEqual(lineA[1].Y(), 10.0) - self.assertAlmostEqual(lineA[1].Z(), 0.0) - lineB = Line3d(Vector3d(1, 2, 3), Vector3d(4, 5, 6)) - self.assertAlmostEqual(lineB[0].X(), 1.0) - self.assertAlmostEqual(lineB[0].Y(), 2.0) - self.assertAlmostEqual(lineB[0].Z(), 3.0) - self.assertAlmostEqual(lineB[1].X(), 4.0) - self.assertAlmostEqual(lineB[1].Y(), 5.0) - self.assertAlmostEqual(lineB[1].Z(), 6.0) - - lineC = Line3d(0, 0, 5, 10, 10, 6) - self.assertAlmostEqual(lineC[0].X(), 0.0) - self.assertAlmostEqual(lineC[0].Y(), 0.0) - self.assertAlmostEqual(lineC[0].Z(), 5.0) - self.assertAlmostEqual(lineC[1].X(), 10.0) - self.assertAlmostEqual(lineC[1].Y(), 10.0) - self.assertAlmostEqual(lineC[1].Z(), 6.0) - - self.assertAlmostEqual(lineB[2].X(), lineB[1].X()) - - def test_set(self): - lineA = Line3d() - lineA.Set(1, 1, 2, 2) - self.assertAlmostEqual(lineA[0].X(), 1.0) - self.assertAlmostEqual(lineA[0].Y(), 1.0) - self.assertAlmostEqual(lineA[0].Z(), 0.0) - self.assertAlmostEqual(lineA[1].X(), 2.0) - self.assertAlmostEqual(lineA[1].Y(), 2.0) - self.assertAlmostEqual(lineA[1].Z(), 0.0) - - lineA.Set(10, 11, 12, 13, 14, 15) - self.assertAlmostEqual(lineA[0].X(), 10.0) - self.assertAlmostEqual(lineA[0].Y(), 11.0) - self.assertAlmostEqual(lineA[0].Z(), 12.0) - self.assertAlmostEqual(lineA[1].X(), 13.0) - self.assertAlmostEqual(lineA[1].Y(), 14.0) - self.assertAlmostEqual(lineA[1].Z(), 15.0) - - lineA.SetA(Vector3d(0, -1, -2)) - self.assertAlmostEqual(lineA[0].X(), 0.0) - self.assertAlmostEqual(lineA[0].Y(), -1.0) - self.assertAlmostEqual(lineA[0].Z(), -2.0) - self.assertAlmostEqual(lineA[1].X(), 13.0) - self.assertAlmostEqual(lineA[1].Y(), 14.0) - self.assertAlmostEqual(lineA[1].Z(), 15.0) - - lineA.SetB(Vector3d(5, 6, 7)) - self.assertAlmostEqual(lineA[0].X(), 0.0) - self.assertAlmostEqual(lineA[0].Y(), -1.0) - self.assertAlmostEqual(lineA[0].Z(), -2.0) - self.assertAlmostEqual(lineA[1].X(), 5.0) - self.assertAlmostEqual(lineA[1].Y(), 6.0) - self.assertAlmostEqual(lineA[1].Z(), 7.0) - - def test_length(self): - lineA = Line3d(0, 0, 0, 10, 10, 10) - self.assertAlmostEqual(lineA.Length(), math.sqrt(300), delta=1e-10) - - def test_equality(self): - lineA = Line3d(1, 1, 1, 2, 1, 2) - lineB = Line3d(1, 2, 3, 2, 2, 4) - - self.assertTrue(lineA != lineB) - self.assertTrue(lineA == lineA) - - lineB.Set(1, 1, 1, 2, 1.1, 2) - self.assertFalse(lineA == lineB) - - lineB.Set(1, 1, 1, 2.1, 1, 2) - self.assertFalse(lineA == lineB) - - lineB.Set(1, 1, 1.1, 2, 1, 2) - self.assertFalse(lineA == lineB) - - lineB.Set(1.1, 1, 1, 2, 1, 2) - self.assertFalse(lineA == lineB) - - def test_serialization(self): - line = Line3d(0, 1, 4, 2, 3, 7) - self.assertEqual(str(line), "0 1 4 2 3 7") - - def test_copy_constructor(self): - lineA = Line3d(0, 1, 4, 2, 3, 7) - lineB = Line3d(lineA) - - self.assertEqual(lineA, lineB) - - def test_direction(self): - lineA = Line3d(1, 1, 1, 0, 0, 0) - lineB = Line3d(2, 2, 2, 0, 0, 0) - lineC = Line3d(0, 0, 0, 1, 1, 1) - self.assertTrue(lineA.Direction() == (lineA[1] - lineA[0]).Normalize()) - self.assertTrue(lineA.Direction() == lineB.Direction()) - self.assertFalse(lineA.Direction() == lineC.Direction()) - - lineA.Set(1, 1, 2, 1, 1, 10) - self.assertTrue(lineA.Direction() == Vector3d.UnitZ) - - lineA.Set(1, 5, 1, 1, 1, 1) - self.assertTrue(lineA.Direction() == -Vector3d.UnitY) - - lineA.Set(1, 1, 1, 7, 1, 1) - self.assertTrue(lineA.Direction() == Vector3d.UnitX) - - def test_within(self): - line = Line3d(0, 0, 0, 1, 1, 1) - self.assertTrue(line.Within(Vector3d(0, 0, 0))) - self.assertTrue(line.Within(Vector3d(1, 1, 1))) - self.assertTrue(line.Within(Vector3d(0.5, 0.5, 0.5))) - - self.assertFalse(line.Within(Vector3d(-0.5, 0.5, 0.5))) - self.assertFalse(line.Within(Vector3d(0.5, -0.5, 0.5))) - self.assertFalse(line.Within(Vector3d(0.5, 0.5, -0.5))) - - def test_distance(self): - line = Line3d(0, 0, 0, 0, 1, 0) - result = Line3d() - - self.assertTrue(line.Distance(Line3d(1, 0.5, 0, -1, 0.5, 0), result)) - self.assertAlmostEqual(result.Length(), 0) - self.assertEqual(result, Line3d(0, 0.5, 0, 0, 0.5, 0)) - - self.assertTrue(line.Distance(Line3d(1, 0, 0, -1, 0, 0), result)) - self.assertAlmostEqual(result.Length(), 0) - self.assertEqual(result, Line3d(0, 0, 0, 0, 0, 0)) - - self.assertTrue(line.Distance(Line3d(1, 1.1, 0, -1, 1.1, 0), result)) - self.assertAlmostEqual(result.Length(), 0.1, delta=1e-4) - self.assertEqual(result, Line3d(0, 1, 0, 0, 1.1, 0)) - - self.assertTrue(line.Distance(Line3d(1, 0.5, 0.4, -1, 0.5, 0.4), - result)) - self.assertAlmostEqual(result.Length(), 0.4, delta=1e-4) - self.assertEqual(result, Line3d(0, 0.5, 0, 0, 0.5, 0.4)) - - self.assertTrue(line.Distance(Line3d(0, 0.5, 1, 1, 0.5, 0), - result)) - self.assertAlmostEqual(result.Length(), math.sin(math.pi / 4), - delta=1e-4) - self.assertEqual(result, Line3d(0, 0.5, 0, 0.5, 0.5, 0.5)) - - # Expect true when lines are parallel - self.assertTrue(line.Distance(Line3d(2, 0, 0, 2, 1, 0), result)) - self.assertEqual(result[0], line[0]) - self.assertEqual(result[1], Vector3d(2, 0, 0)) - - self.assertTrue(line.Distance(Line3d(2, 1, 0, 2, 0, 0), result)) - self.assertEqual(result[0], line[0]) - self.assertEqual(result[1], Vector3d(2, 0, 0)) - - self.assertTrue(line.Distance(Line3d(1, 1, 0, 1, 2, 0), result)) - self.assertEqual(result[0], line[1]) - self.assertEqual(result[1], Vector3d(1, 1, 0)) - - self.assertTrue(line.Distance(Line3d(1, 2, 0, 1, 1, 0), result)) - self.assertEqual(result[0], line[1]) - self.assertEqual(result[1], Vector3d(1, 1, 0)) - - # Expect false when the passed in line is a point - self.assertFalse(line.Distance(Line3d(2, 0, 0, 2, 0, 0), result)) - - # Expect false when the first line is a point. - line.Set(0, 0, 0, 0, 0, 0) - self.assertFalse(line.Distance(Line3d(2, 0, 0, 2, 1, 0), result)) - - def test_interesct(self): - line = Line3d(0, 0, 0, 0, 1, 0) - pt = Vector3d() - - self.assertTrue(line.Intersect(Line3d(1, 0.5, 0, -1, 0.5, 0))) - self.assertTrue(line.Intersect(Line3d(1, 0.5, 0, -1, 0.5, 0), pt)) - self.assertEqual(pt, Vector3d(0, 0.5, 0)) - - self.assertTrue(line.Intersect(Line3d(1, 0, 0, -1, 0, 0))) - self.assertTrue(line.Intersect(Line3d(1, 0, 0, -1, 0, 0), pt)) - self.assertEqual(pt, Vector3d(0, 0, 0)) - - self.assertTrue(line.Intersect(Line3d(1, 1, 0, -1, 1, 0))) - self.assertTrue(line.Intersect(Line3d(1, 1, 0, -1, 1, 0), pt)) - self.assertEqual(pt, Vector3d(0, 1, 0)) - - self.assertTrue(line.Intersect(Line3d(0, 0.5, -1, 0, 0.5, 1))) - self.assertTrue(line.Intersect(Line3d(0, 0.5, -1, 0, 0.5, 1), pt)) - self.assertEqual(pt, Vector3d(0, 0.5, 0)) - - self.assertTrue(line.Intersect(Line3d(-1, 0.5, -1, 1, 0.5, 1))) - self.assertTrue(line.Intersect(Line3d(-1, 0.5, -1, 1, 0.5, 1), pt)) - self.assertEqual(pt, Vector3d(0, 0.5, 0)) - - self.assertFalse(line.Intersect(Line3d(1, 1.1, 0, -1, 1.1, 0))) - self.assertFalse(line.Intersect(Line3d(1, -0.1, 0, -1, -0.1, 0))) - - self.assertFalse(line.Intersect(Line3d(0.1, 0.1, 0, 0.6, 0.6, 0))) - self.assertFalse(line.Intersect(Line3d(-0.1, 0, 0, -0.1, 1, 0))) - - self.assertTrue(line.Intersect(Line3d(0, -1, 0, 0, 0.1, 0))) - self.assertTrue(line.Intersect(Line3d(0, 1, 0, 0, 1.1, 0))) - - def test_parallel(self): - line = Line3d(0, 0, 0, 0, 1, 0) - self.assertTrue(line.Parallel(Line3d(1, 0, 0, 1, 1, 0))) - self.assertTrue(line.Parallel(Line3d(1, 1, 0, 1, 0, 0))) - self.assertTrue(line.Parallel(Line3d(0, 0, 0, 0, 10, 0))) - self.assertTrue(line.Parallel(Line3d(-100, 100, 20, -100, 200, 20))) - - self.assertFalse(line.Parallel(Line3d(1, 0, 0, 1, 1, 1))) - self.assertFalse(line.Parallel(Line3d(1, 0, 0, 2, 0, 0))) - self.assertFalse(line.Parallel(Line3d(1, 0, 1, 2, 0, 1))) - - def test_coplanar(self): - line = Line3d(0, 0, 0, 0, 1, 0) - self.assertTrue(line.Coplanar(Line3d(1, 0, 0, 1, 1, 0))) - self.assertTrue(line.Coplanar(Line3d(0, 0, 0, 0, 10, 0))) - self.assertTrue(line.Coplanar(Line3d(-100, 100, 20, -100, 200, 20))) - - self.assertFalse(line.Coplanar(Line3d(1, 0, 0, 1, 1, 1))) - self.assertFalse(line.Coplanar(Line3d(1, 0, 1, 2, 0, 0))) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/SignalStats.i b/src/SignalStats.i deleted file mode 100644 index 521ba063d..000000000 --- a/src/SignalStats.i +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2021 Open Source Robotics Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - -%module signalStats - -%{ -#include -%} - -%include "std_string.i" -%include "std_map.i" -%template(map_string_double) std::map; - -namespace ignition -{ - namespace math - { - class SignalStatistic - { - public: SignalStatistic(); - public: virtual ~SignalStatistic(); - public: SignalStatistic(const SignalStatistic &_ss); - public: virtual double Value() const = 0; - public: virtual std::string ShortName() const = 0; - public: virtual size_t Count() const; - public: virtual void InsertData(const double _data) = 0; - public: virtual void Reset(); - }; - - class SignalMaximum : public SignalStatistic - { - public: virtual double Value() const override; - public: virtual std::string ShortName() const override; - public: virtual void InsertData(const double _data) override; - }; - - class SignalMean : public SignalStatistic - { - public: virtual double Value() const override; - public: virtual std::string ShortName() const override; - public: virtual void InsertData(const double _data) override; - }; - - class SignalMinimum : public SignalStatistic - { - public: virtual double Value() const override; - public: virtual std::string ShortName() const override; - public: virtual void InsertData(const double _data) override; - }; - - class SignalRootMeanSquare : public SignalStatistic - { - public: virtual double Value() const override; - public: virtual std::string ShortName() const override; - public: virtual void InsertData(const double _data) override; - }; - - class SignalMaxAbsoluteValue : public SignalStatistic - { - public: virtual double Value() const override; - public: virtual std::string ShortName() const override; - public: virtual void InsertData(const double _data) override; - }; - - class SignalVariance : public SignalStatistic - { - public: virtual double Value() const override; - public: virtual std::string ShortName() const override; - public: virtual void InsertData(const double _data) override; - }; - - class SignalStats - { - public: SignalStats(); - public: ~SignalStats(); - public: SignalStats(const SignalStats &_ss); - public: size_t Count() const; - public: std::map Map() const; - public: void InsertData(const double _data); - public: bool InsertStatistic(const std::string &_name); - public: bool InsertStatistics(const std::string &_names); - public: void Reset(); - }; - - } -} \ No newline at end of file diff --git a/src/SignalStats_TEST.py b/src/SignalStats_TEST.py deleted file mode 100644 index 51d88235c..000000000 --- a/src/SignalStats_TEST.py +++ /dev/null @@ -1,528 +0,0 @@ -# Copyright (C) 2021 Open Source Robotics Foundation - -# Licensed under the Apache License, Version 2.0 (the "License") -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -import math -from ignition.math import Rand -from ignition.math import SignalMaximum -from ignition.math import SignalMinimum -from ignition.math import SignalMean -from ignition.math import SignalVariance -from ignition.math import SignalRootMeanSquare -from ignition.math import SignalMaxAbsoluteValue -from ignition.math import SignalStats -from ignition.math import SignalStatistic - - -class TestSignalStats(unittest.TestCase): - - def test_signal_maximum_constructor(self): - # Constructor - max = SignalMaximum() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - self.assertEqual(max.ShortName(), "max") - - # Reset - max.Reset() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - def test_signal_maximum_constant_values(self): - # Constant values, max should match - max = SignalMaximum() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - max.InsertData(value) - self.assertAlmostEqual(max.Value(), value) - self.assertEqual(max.Count(), i) - - # Reset - max.Reset() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - def test_signal_maximum_alternating_values(self): - # Values with alternating sign, increasing magnitude - # Should always match positive value - max = SignalMaximum() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - - for i in range(1, 11): - max.InsertData(value * i) - self.assertAlmostEqual(max.Value(), value * i) - max.InsertData(-value * i) - self.assertAlmostEqual(max.Value(), value * i) - self.assertEqual(max.Count(), i*2) - - # Reset - max.Reset() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - def test_signal_mean_constructor(self): - # Constructor - mean = SignalMean() - self.assertAlmostEqual(mean.Value(), 0.0) - self.assertEqual(mean.Count(), 0) - self.assertEqual(mean.ShortName(), "mean") - - # Reset - mean.Reset() - self.assertAlmostEqual(mean.Value(), 0.0) - self.assertEqual(mean.Count(), 0) - - def test_signal_mean_constant_values(self): - # Constant values, mean should match - mean = SignalMean() - self.assertAlmostEqual(mean.Value(), 0.0) - self.assertEqual(mean.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - mean.InsertData(value) - self.assertAlmostEqual(mean.Value(), value) - self.assertEqual(mean.Count(), i) - - # Reset - mean.Reset() - self.assertAlmostEqual(mean.Value(), 0.0) - self.assertEqual(mean.Count(), 0) - - def test_signal_mean_alternating_values(self): - # Values with alternating sign, increasing magnitude - # Should be zero every other time - mean = SignalMean() - self.assertAlmostEqual(mean.Value(), 0.0) - self.assertEqual(mean.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - mean.InsertData(value * i) - mean.InsertData(-value * i) - self.assertAlmostEqual(mean.Value(), 0.0) - self.assertEqual(mean.Count(), i*2) - - # Reset - mean.Reset() - self.assertAlmostEqual(mean.Value(), 0.0) - self.assertEqual(mean.Count(), 0) - - def test_signal_minimum_constructor(self): - # Constructor - min = SignalMinimum() - self.assertAlmostEqual(min.Value(), 0.0) - self.assertEqual(min.Count(), 0) - self.assertEqual(min.ShortName(), "min") - - # Reset - min.Reset() - self.assertAlmostEqual(min.Value(), 0.0) - self.assertEqual(min.Count(), 0) - - def test_signal_minimum_constant_values(self): - # Constant values, min should match - min = SignalMinimum() - self.assertAlmostEqual(min.Value(), 0.0) - self.assertEqual(min.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - min.InsertData(value) - self.assertAlmostEqual(min.Value(), value) - self.assertEqual(min.Count(), i) - - # Reset - min.Reset() - self.assertAlmostEqual(min.Value(), 0.0) - self.assertEqual(min.Count(), 0) - - def test_signal_minimum_alternating_values(self): - # Values with alternating sign, increasing magnitude - # Should always match negative value - min = SignalMinimum() - self.assertAlmostEqual(min.Value(), 0.0) - self.assertEqual(min.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - min.InsertData(value * i) - min.InsertData(-value * i) - self.assertAlmostEqual(min.Value(), -value * i) - self.assertEqual(min.Count(), i*2) - - # Reset - min.Reset() - self.assertAlmostEqual(min.Value(), 0.0) - self.assertEqual(min.Count(), 0) - - def test_signal_root_mean_square(self): - # Constructor - rms = SignalRootMeanSquare() - self.assertAlmostEqual(rms.Value(), 0.0) - self.assertEqual(rms.Count(), 0) - self.assertEqual(rms.ShortName(), "rms") - - # Reset - rms.Reset() - self.assertAlmostEqual(rms.Value(), 0.0) - self.assertEqual(rms.Count(), 0) - - def test_signal_root_mean_square_constant_values(self): - # Constant values, rms should match - rms = SignalRootMeanSquare() - self.assertAlmostEqual(rms.Value(), 0.0) - self.assertEqual(rms.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - rms.InsertData(value) - self.assertAlmostEqual(rms.Value(), value) - self.assertEqual(rms.Count(), i) - - # Reset - rms.Reset() - self.assertAlmostEqual(rms.Value(), 0.0) - self.assertEqual(rms.Count(), 0) - - def test_signal_root_mean_square_alternating_values(self): - # Values with alternating sign, same magnitude - # rms should match absolute value every time - rms = SignalRootMeanSquare() - self.assertAlmostEqual(rms.Value(), 0.0) - self.assertEqual(rms.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - rms.InsertData(value) - self.assertAlmostEqual(rms.Value(), value) - self.assertEqual(rms.Count(), i*2-1) - - rms.InsertData(-value) - self.assertAlmostEqual(rms.Value(), value) - self.assertEqual(rms.Count(), i*2) - - # Reset - rms.Reset() - self.assertAlmostEqual(rms.Value(), 0.0) - self.assertEqual(rms.Count(), 0) - - def test_signal_max_absolute_value_constructor(self): - # Constructor - max = SignalMaxAbsoluteValue() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - self.assertEqual(max.ShortName(), "maxAbs") - - # Reset - max.Reset() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - def test_signal_max_absolute_value_constant_values(self): - # Constant values, max should match - max = SignalMaxAbsoluteValue() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - max.InsertData(value) - self.assertAlmostEqual(max.Value(), value) - self.assertEqual(max.Count(), i) - - # Reset - max.Reset() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - def test_signal_max_absolute_value_alternating_values(self): - # Values with alternating sign, increasing magnitude - # max should match absolute value every time - max = SignalMaxAbsoluteValue() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - max.InsertData(value * i) - self.assertAlmostEqual(max.Value(), value * i) - self.assertEqual(max.Count(), i*2-1) - - max.InsertData(-value * i) - self.assertAlmostEqual(max.Value(), value * i) - self.assertEqual(max.Count(), i*2) - - # Reset - max.Reset() - self.assertAlmostEqual(max.Value(), 0.0) - self.assertEqual(max.Count(), 0) - - def test_signal_variance_constructor(self): - var = SignalVariance() - self.assertAlmostEqual(var.Value(), 0.0) - self.assertEqual(var.Count(), 0) - self.assertEqual(var.ShortName(), "var") - - # Reset - var.Reset() - self.assertAlmostEqual(var.Value(), 0.0) - self.assertEqual(var.Count(), 0) - - def test_signal_variance_one_value(self): - # Add one value, expect 0.0 variance - values = {0, 1.0, 10.0, -100.0} - for value in values: - var = SignalVariance() - var.InsertData(value) - self.assertEqual(var.Count(), 1) - self.assertAlmostEqual(0.0, var.Value()) - - # Reset - var.Reset() - self.assertAlmostEqual(0.0, var.Value()) - self.assertEqual(var.Count(), 0) - - def test_signal_variance_constant_values(self): - # Constant values, expect 0.0 variance - var = SignalVariance() - value = 3.14159 - - # Loop two times to verify Reset - for j in range(2): - for i in range(1, 11): - var.InsertData(value) - self.assertAlmostEqual(0.0, var.Value()) - self.assertEqual(var.Count(), i) - - # Reset - var.Reset() - self.assertAlmostEqual(var.Value(), 0.0) - self.assertEqual(var.Count(), 0) - - def test_signal_variance_random_values(self): - # Random normally distributed values - # The sample variance has the following variance: - # 2 variance^2 / (count - 1) - # en.wikipedia.org/wiki/Variance#Distribution_of_the_sample_variance - # We will use 5 sigma (4e-5 chance of failure) - var = SignalVariance() - stdDev = 3.14159 - count = 10000 - sigma = 5.0 - for i in range(count): - var.InsertData(Rand.DblNormal(0.0, stdDev)) - - variance = stdDev*stdDev - sampleVariance2 = 2 * variance*variance / (count - 1) - self.assertAlmostEqual(var.Value(), variance, - delta=sigma*math.sqrt(sampleVariance2)) - - # Reset - var.Reset() - self.assertAlmostEqual(var.Value(), 0.0) - self.assertEqual(var.Count(), 0) - - def test_signal_stats_constructor(self): - # Constructor - stats = SignalStats() - self.assertTrue(stats.Map().empty()) - self.assertEqual(stats.Count(), 0) - - stats2 = SignalStats(stats) - self.assertEqual(stats.Count(), stats2.Count()) - - # Reset - stats.Reset() - self.assertTrue(stats.Map().empty()) - self.assertEqual(stats.Count(), 0) - - def test_01_signal_stats_intern_statistic(self): - # InsertStatistic - stats = SignalStats() - self.assertTrue(stats.Map().empty()) - - self.assertTrue(stats.InsertStatistic("max")) - self.assertFalse(stats.InsertStatistic("max")) - self.assertFalse(stats.Map().empty()) - - self.assertTrue(stats.InsertStatistic("maxAbs")) - self.assertFalse(stats.InsertStatistic("maxAbs")) - self.assertFalse(stats.Map().empty()) - - self.assertTrue(stats.InsertStatistic("mean")) - self.assertFalse(stats.InsertStatistic("mean")) - self.assertFalse(stats.Map().empty()) - - self.assertTrue(stats.InsertStatistic("min")) - self.assertFalse(stats.InsertStatistic("min")) - self.assertFalse(stats.Map().empty()) - - self.assertTrue(stats.InsertStatistic("rms")) - self.assertFalse(stats.InsertStatistic("rms")) - self.assertFalse(stats.Map().empty()) - - self.assertTrue(stats.InsertStatistic("var")) - self.assertFalse(stats.InsertStatistic("var")) - self.assertFalse(stats.Map().empty()) - - self.assertFalse(stats.InsertStatistic("FakeStatistic")) - - # Map with no data - map = stats.Map() - self.assertFalse(map.empty()) - self.assertEqual(map.size(), 6) - self.assertEqual(map.count("max"), 1) - self.assertEqual(map.count("maxAbs"), 1) - self.assertEqual(map.count("mean"), 1) - self.assertEqual(map.count("min"), 1) - self.assertEqual(map.count("rms"), 1) - self.assertEqual(map.count("var"), 1) - self.assertEqual(map.count("FakeStatistic"), 0) - - stats2 = SignalStats(stats) - map2 = stats2.Map() - self.assertFalse(map2.empty()) - self.assertEqual(map.size(), map2.size()) - self.assertEqual(map.count("max"), map2.count("max")) - self.assertEqual(map.count("maxAbs"), map2.count("maxAbs")) - self.assertEqual(map.count("mean"), map2.count("mean")) - self.assertEqual(map.count("min"), map2.count("min")) - self.assertEqual(map.count("rms"), map2.count("rms")) - self.assertEqual(map.count("var"), map2.count("var")) - self.assertEqual(map.count("FakeStatistic"), - map2.count("FakeStatistic")) - - def test_02_signal_stats_intern_statistic(self): - # InsertStatistics - stats = SignalStats() - self.assertFalse(stats.InsertStatistics("")) - self.assertTrue(stats.Map().empty()) - - self.assertTrue(stats.InsertStatistics("maxAbs,rms")) - self.assertEqual(stats.Map().size(), 2) - self.assertFalse(stats.InsertStatistics("maxAbs,rms")) - self.assertFalse(stats.InsertStatistics("maxAbs")) - self.assertFalse(stats.InsertStatistics("rms")) - self.assertEqual(stats.Map().size(), 2) - - self.assertFalse(stats.InsertStatistics("mean,FakeStatistic")) - self.assertEqual(stats.Map().size(), 3) - - self.assertFalse(stats.InsertStatistics("var,FakeStatistic")) - self.assertEqual(stats.Map().size(), 4) - - self.assertFalse(stats.InsertStatistics("max,FakeStatistic")) - self.assertEqual(stats.Map().size(), 5) - - self.assertFalse(stats.InsertStatistics("min,FakeStatistic")) - self.assertEqual(stats.Map().size(), 6) - - self.assertFalse(stats.InsertStatistics("FakeStatistic")) - self.assertEqual(stats.Map().size(), 6) - - # Map with no data - map = stats.Map() - self.assertFalse(map.empty()) - self.assertEqual(map.size(), 6) - self.assertEqual(map.count("max"), 1) - self.assertEqual(map.count("maxAbs"), 1) - self.assertEqual(map.count("mean"), 1) - self.assertEqual(map.count("min"), 1) - self.assertEqual(map.count("rms"), 1) - self.assertEqual(map.count("var"), 1) - self.assertEqual(map.count("FakeStatistic"), 0) - - def test_signal_stats_alternating_values(self): - # Add some statistics - stats = SignalStats() - self.assertTrue(stats.InsertStatistics("max,maxAbs,mean,min,rms")) - self.assertEqual(stats.Map().size(), 5) - - # No data yet - self.assertEqual(stats.Count(), 0) - - # Insert data with alternating signs - value = 3.14159 - stats.InsertData(value) - stats.InsertData(-value) - self.assertEqual(stats.Count(), 2) - - map = stats.Map() - self.assertAlmostEqual(map["max"], value) - self.assertAlmostEqual(map["maxAbs"], value) - self.assertAlmostEqual(map["min"], -value) - self.assertAlmostEqual(map["rms"], value) - self.assertAlmostEqual(map["mean"], 0.0) - - # test operator= - copy = SignalStats(stats) - self.assertEqual(copy.Count(), 2) - map = stats.Map() - self.assertEqual(map.size(), 5) - self.assertAlmostEqual(map["max"], value) - self.assertAlmostEqual(map["maxAbs"], value) - self.assertAlmostEqual(map["min"], -value) - self.assertAlmostEqual(map["rms"], value) - self.assertAlmostEqual(map["mean"], 0.0) - - stats.Reset() - self.assertEqual(stats.Map().size(), 5) - self.assertEqual(stats.Count(), 0) - map = stats.Map() - self.assertAlmostEqual(map["max"], 0.0) - self.assertAlmostEqual(map["maxAbs"], 0.0) - self.assertAlmostEqual(map["min"], 0.0) - self.assertAlmostEqual(map["rms"], 0.0) - self.assertAlmostEqual(map["mean"], 0.0) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 2d502ff5f..997ef3942 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -58,9 +58,9 @@ if (PYTHONLIBS_FOUND) # Suppress warnings on SWIG-generated files target_compile_options(${SWIG_PY_LIB} PRIVATE - $<$:-Wno-pedantic -Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-cast-function-type -Wno-missing-field-initializers> - $<$:-Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-cast-function-type -Wno-missing-field-initializers> - $<$:-Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-cast-function-type -Wno-missing-field-initializers> + $<$:-Wno-pedantic -Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-cast-function-type -Wno-missing-field-initializers -Wno-class-memaccess> + $<$:-Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-cast-function-type -Wno-missing-field-initializers -Wno-class-memaccess> + $<$:-Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-cast-function-type -Wno-missing-field-initializers -Wno-class-memaccess> ) install(TARGETS ${SWIG_PY_LIB} DESTINATION ${IGN_LIB_INSTALL_DIR}/python/ignition) install(FILES ${CMAKE_BINARY_DIR}/lib/python/math.py DESTINATION ${IGN_LIB_INSTALL_DIR}/python/ignition) @@ -70,11 +70,15 @@ if (PYTHONLIBS_FOUND) set(python_tests Angle_TEST GaussMarkovProcess_TEST + Line2_TEST + Line3_TEST python_TEST Rand_TEST + SignalStats_TEST Vector2_TEST Vector3_TEST Vector4_TEST + Temperature_TEST ) foreach (test ${python_tests}) diff --git a/src/Line2.i b/src/python/Line2.i similarity index 97% rename from src/Line2.i rename to src/python/Line2.i index 196fdee93..ec27c9a7b 100644 --- a/src/Line2.i +++ b/src/python/Line2.i @@ -31,6 +31,7 @@ namespace ignition template class Line2 { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: Line2(const math::Vector2 &_ptA, const math::Vector2 &_ptB); public: Line2(double _x1, double _y1, double _x2, double _y2); public: void Set(const math::Vector2 &_ptA, diff --git a/src/python/Line2_TEST.py b/src/python/Line2_TEST.py new file mode 100644 index 000000000..6ec549dc0 --- /dev/null +++ b/src/python/Line2_TEST.py @@ -0,0 +1,215 @@ +# Copyright (C) 2021 Open Source Robotics Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import math +from ignition.math import Line2d +from ignition.math import Vector2d + + +class TestLine2d(unittest.TestCase): + + def test_construction(self): + lineA = Line2d(0, 0, 10, 10) + self.assertAlmostEqual(lineA[0].x(), 0.0) + self.assertAlmostEqual(lineA[0].y(), 0.0) + self.assertAlmostEqual(lineA[1].x(), 10.0) + self.assertAlmostEqual(lineA[1].y(), 10.0) + + lineB = Line2d(Vector2d(1, 2), Vector2d(3, 4)) + self.assertAlmostEqual(lineB[0].x(), 1.0) + self.assertAlmostEqual(lineB[0].y(), 2.0) + self.assertAlmostEqual(lineB[1].x(), 3.0) + self.assertAlmostEqual(lineB[1].y(), 4.0) + + self.assertAlmostEqual(lineB[2].x(), lineB[1].x()) + + def test_length(self): + lineA = Line2d(0, 0, 10, 10) + self.assertAlmostEqual(lineA.length(), math.sqrt(200), delta=1e-10) + + def test_slope(self): + line = Line2d(0, 0, 10, 10) + self.assertAlmostEqual(line.slope(), 1.0, delta=1e-10) + + line = Line2d(0, 0, 0, 10) + self.assertTrue(math.isnan(line.slope())) + + line = Line2d(-10, 0, 100, 0) + self.assertAlmostEqual(line.slope(), 0.0) + + def test_parallel_line(self): + # Line is always parallel with itself + line = Line2d(0, 0, 10, 0) + self.assertTrue(line.parallel(line, 1e-10)) + + # Degenerate line segment + # Still expect Line is parallel with itself + line = Line2d(0, 0, 0, 0) + self.assertTrue(line.parallel(line, 1e-10)) + + lineA = Line2d(0, 0, 10, 0) + lineB = Line2d(0, 0, 10, 0) + self.assertTrue(lineA.parallel(lineB, 1e-10)) + + lineB.set(0, 0, 0, 10) + self.assertFalse(lineA.parallel(lineB)) + + lineB.set(0, 10, 10, 10) + self.assertTrue(lineA.parallel(lineB)) + + lineB.set(0, 10, 10, 10.00001) + self.assertFalse(lineA.parallel(lineB, 1e-10)) + self.assertFalse(lineA.parallel(lineB)) + self.assertTrue(lineA.parallel(lineB, 1e-3)) + + def test_collinear_line(self): + # Line is always collinear with itself + line = Line2d(0, 0, 10, 0) + self.assertTrue(line.collinear(line, 1e-10)) + + lineA = Line2d(0, 0, 10, 0) + lineB = Line2d(0, 0, 10, 0) + self.assertTrue(lineA.collinear(lineB, 1e-10)) + + lineB.set(0, 10, 10, 10) + self.assertFalse(lineA.collinear(lineB)) + + lineB.set(9, 0, 10, 0.00001) + self.assertFalse(lineA.collinear(lineB, 1e-10)) + self.assertFalse(lineA.collinear(lineB)) + self.assertTrue(lineA.collinear(lineB, 1e-3)) + + def test_collinear_point(self): + lineA = Line2d(0, 0, 10, 0) + pt = Vector2d(0, 0) + self.assertTrue(lineA.collinear(pt)) + + ptLine = Line2d(pt, pt) + self.assertTrue(lineA.collinear(ptLine)) + + pt.set(1000, 0) + self.assertTrue(lineA.collinear(pt, 1e-10)) + + ptLine = Line2d(pt, pt) + self.assertTrue(lineA.parallel(ptLine)) + self.assertFalse(lineA.intersect(ptLine)) + self.assertFalse(lineA.collinear(ptLine, 1e-10)) + + pt.set(10, 0) + ptLine.set(pt, pt) + self.assertTrue(lineA.collinear(ptLine, 1e-10)) + + pt.set(0, 0.00001) + self.assertFalse(lineA.collinear(pt)) + self.assertTrue(lineA.collinear(pt, 1e-3)) + + ptLine = Line2d(pt, pt) + self.assertFalse(lineA.collinear(ptLine)) + self.assertTrue(lineA.parallel(ptLine)) + self.assertFalse(lineA.intersect(ptLine)) + self.assertTrue(lineA.intersect(ptLine, 1e-2)) + self.assertTrue(lineA.collinear(ptLine, 1e-3)) + + pt.set(0, -0.00001) + self.assertFalse(lineA.collinear(pt)) + self.assertTrue(lineA.collinear(pt, 1e-3)) + + ptLine = Line2d(pt, pt) + self.assertFalse(lineA.collinear(ptLine)) + self.assertTrue(lineA.collinear(ptLine, 1e-4)) + + def test_intersect(self): + pt = Vector2d() + + # parallel horizontal lines + lineA = Line2d(1, 1, 2, 1) + lineB = Line2d(1, 2, 2, 2) + self.assertFalse(lineA.intersect(lineB, pt)) + + # parallel vertical lines + lineA.set(1, 1, 1, 10) + lineB.set(2, 1, 2, 10) + self.assertFalse(lineA.intersect(lineB, pt)) + + # Two lines that form an inverted T with a gap + lineA.set(1, 1, 1, 10) + lineB.set(0, 0, 2, 0) + self.assertFalse(lineA.intersect(lineB, pt)) + + # Two lines that form a T with a gap + lineA.set(1, 1, 1, 10) + lineB.set(0, 10.1, 2, 10.1) + self.assertFalse(lineA.intersect(lineB, pt)) + + # Two lines that form an inverted T with a gap + lineA.set(0, -10, 0, 10) + lineB.set(1, 0, 10, 0) + self.assertFalse(lineA.intersect(lineB, pt)) + + # Two lines that form a T with a gap + lineA.set(0, -10, 0, 10) + lineB.set(-1, 0, -10, 0) + self.assertFalse(lineA.intersect(lineB, pt)) + + # Two collinear lines, one starts where the other stopped + lineA.set(1, 1, 1, 10) + lineB.set(1, 10, 1, 11) + self.assertTrue(lineA.intersect(lineB, pt)) + self.assertEqual(pt, Vector2d(1, 10)) + + # Two collinear lines, one overlaps the other + lineA.set(0, 0, 0, 10) + lineB.set(0, 9, 0, 11) + self.assertTrue(lineA.intersect(lineB, pt)) + self.assertEqual(pt, Vector2d(0, 9)) + + # Two collinear lines, one overlaps the other + lineA.set(0, 0, 0, 10) + lineB.set(0, -10, 0, 1) + self.assertTrue(lineA.intersect(lineB, pt)) + self.assertEqual(pt, Vector2d(0, 1)) + + # Two intersecting lines + lineA.set(0, 0, 10, 10) + lineB.set(0, 10, 10, 0) + self.assertTrue(lineA.intersect(lineB, pt)) + self.assertEqual(pt, Vector2d(5, 5)) + + def test_equality(self): + lineA = Line2d(1, 1, 2, 1) + lineB = Line2d(1, 2, 2, 2) + + self.assertTrue(lineA != lineB) + self.assertTrue(lineA == lineA) + + lineB.set(1, 1, 2, 1.1) + self.assertFalse(lineA == lineB) + + lineB.set(1, 1, 2.1, 1) + self.assertFalse(lineA == lineB) + + lineB.set(1, 1.1, 2, 1) + self.assertFalse(lineA == lineB) + + lineB.set(1.1, 1, 2, 1) + self.assertFalse(lineA == lineB) + + def test_serialization(self): + line = Line2d(0, 1, 2, 3) + self.assertEqual(str(line), "0 1 2 3") + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/Line3.i b/src/python/Line3.i new file mode 100644 index 000000000..2e3b2c3d2 --- /dev/null +++ b/src/python/Line3.i @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +%module line3 +%{ +#include +#include +#include +%} + +%include "std_string.i" + +namespace ignition +{ + namespace math + { + template + class Line3 + { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; + public: Line3() = default; + public: Line3(const Line3 &_line); + public: Line3(const math::Vector3 &_ptA, const math::Vector3 &_ptB); + public: Line3(const double _x1, const double _y1, + const double _x2, const double _y2); + public: Line3(const double _x1, const double _y1, + const double _z1, const double _x2, + const double _y2, const double _z2); + public: void Set(const math::Vector3 &_ptA, + const math::Vector3 &_ptB); + public: void SetA(const math::Vector3 &_ptA); + public: void SetB(const math::Vector3 &_ptB); + public: void Set(const double _x1, const double _y1, + const double _x2, const double _y2, + const double _z = 0); + public: void Set(const double _x1, const double _y1, + const double _z1, const double _x2, + const double _y2, const double _z2); + public: math::Vector3 Direction() const; + public: T Length() const; + public: bool Distance(const Line3 &_line, Line3 &_result, + const double _epsilon = 1e-6) const; + public: bool Intersect(const Line3 &_line, + double _epsilon = 1e-6) const; + public: bool Coplanar(const Line3 &_line, + const double _epsilon = 1e-6) const; + public: bool Parallel(const Line3 &_line, + const double _epsilon = 1e-6) const; + public: bool Intersect(const Line3 &_line, math::Vector3 &_pt, + double _epsilon = 1e-6) const; + public: bool Within(const math::Vector3 &_pt, + double _epsilon = 1e-6) const; + public: bool operator==(const Line3 &_line) const; + public: bool operator!=(const Line3 &_line) const; + }; + + %extend Line3 + { + ignition::math::Vector3 __getitem__(const unsigned int i) const + { + return (*$self)[i]; + } + } + + %extend Line3 + { + std::string __str__() const { + std::ostringstream out; + out << *$self; + return out.str(); + } + } + + %template(Line3i) Line3; + %template(Line3d) Line3; + %template(Line3f) Line3; + } +} diff --git a/src/python/Line3_TEST.py b/src/python/Line3_TEST.py new file mode 100644 index 000000000..eff3eed2c --- /dev/null +++ b/src/python/Line3_TEST.py @@ -0,0 +1,249 @@ +# Copyright (C) 2021 Open Source Robotics Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import math +from ignition.math import Line3d +from ignition.math import Vector3d + + +class TestLine3d(unittest.TestCase): + + def test_construction(self): + lineA = Line3d(0, 0, 10, 10) + self.assertAlmostEqual(lineA[0].x(), 0.0) + self.assertAlmostEqual(lineA[0].y(), 0.0) + self.assertAlmostEqual(lineA[0].z(), 0.0) + self.assertAlmostEqual(lineA[1].x(), 10.0) + self.assertAlmostEqual(lineA[1].y(), 10.0) + self.assertAlmostEqual(lineA[1].z(), 0.0) + lineB = Line3d(Vector3d(1, 2, 3), Vector3d(4, 5, 6)) + self.assertAlmostEqual(lineB[0].x(), 1.0) + self.assertAlmostEqual(lineB[0].y(), 2.0) + self.assertAlmostEqual(lineB[0].z(), 3.0) + self.assertAlmostEqual(lineB[1].x(), 4.0) + self.assertAlmostEqual(lineB[1].y(), 5.0) + self.assertAlmostEqual(lineB[1].z(), 6.0) + + lineC = Line3d(0, 0, 5, 10, 10, 6) + self.assertAlmostEqual(lineC[0].x(), 0.0) + self.assertAlmostEqual(lineC[0].y(), 0.0) + self.assertAlmostEqual(lineC[0].z(), 5.0) + self.assertAlmostEqual(lineC[1].x(), 10.0) + self.assertAlmostEqual(lineC[1].y(), 10.0) + self.assertAlmostEqual(lineC[1].z(), 6.0) + + self.assertAlmostEqual(lineB[2].x(), lineB[1].x()) + + def test_set(self): + lineA = Line3d() + lineA.set(1, 1, 2, 2) + self.assertAlmostEqual(lineA[0].x(), 1.0) + self.assertAlmostEqual(lineA[0].y(), 1.0) + self.assertAlmostEqual(lineA[0].z(), 0.0) + self.assertAlmostEqual(lineA[1].x(), 2.0) + self.assertAlmostEqual(lineA[1].y(), 2.0) + self.assertAlmostEqual(lineA[1].z(), 0.0) + + lineA.set(10, 11, 12, 13, 14, 15) + self.assertAlmostEqual(lineA[0].x(), 10.0) + self.assertAlmostEqual(lineA[0].y(), 11.0) + self.assertAlmostEqual(lineA[0].z(), 12.0) + self.assertAlmostEqual(lineA[1].x(), 13.0) + self.assertAlmostEqual(lineA[1].y(), 14.0) + self.assertAlmostEqual(lineA[1].z(), 15.0) + + lineA.set_a(Vector3d(0, -1, -2)) + self.assertAlmostEqual(lineA[0].x(), 0.0) + self.assertAlmostEqual(lineA[0].y(), -1.0) + self.assertAlmostEqual(lineA[0].z(), -2.0) + self.assertAlmostEqual(lineA[1].x(), 13.0) + self.assertAlmostEqual(lineA[1].y(), 14.0) + self.assertAlmostEqual(lineA[1].z(), 15.0) + + lineA.set_b(Vector3d(5, 6, 7)) + self.assertAlmostEqual(lineA[0].x(), 0.0) + self.assertAlmostEqual(lineA[0].y(), -1.0) + self.assertAlmostEqual(lineA[0].z(), -2.0) + self.assertAlmostEqual(lineA[1].x(), 5.0) + self.assertAlmostEqual(lineA[1].y(), 6.0) + self.assertAlmostEqual(lineA[1].z(), 7.0) + + def test_length(self): + lineA = Line3d(0, 0, 0, 10, 10, 10) + self.assertAlmostEqual(lineA.length(), math.sqrt(300), delta=1e-10) + + def test_equality(self): + lineA = Line3d(1, 1, 1, 2, 1, 2) + lineB = Line3d(1, 2, 3, 2, 2, 4) + + self.assertTrue(lineA != lineB) + self.assertTrue(lineA == lineA) + + lineB.set(1, 1, 1, 2, 1.1, 2) + self.assertFalse(lineA == lineB) + + lineB.set(1, 1, 1, 2.1, 1, 2) + self.assertFalse(lineA == lineB) + + lineB.set(1, 1, 1.1, 2, 1, 2) + self.assertFalse(lineA == lineB) + + lineB.set(1.1, 1, 1, 2, 1, 2) + self.assertFalse(lineA == lineB) + + def test_serialization(self): + line = Line3d(0, 1, 4, 2, 3, 7) + self.assertEqual(str(line), "0 1 4 2 3 7") + + def test_copy_constructor(self): + lineA = Line3d(0, 1, 4, 2, 3, 7) + lineB = Line3d(lineA) + + self.assertEqual(lineA, lineB) + + def test_direction(self): + lineA = Line3d(1, 1, 1, 0, 0, 0) + lineB = Line3d(2, 2, 2, 0, 0, 0) + lineC = Line3d(0, 0, 0, 1, 1, 1) + self.assertTrue(lineA.direction() == (lineA[1] - lineA[0]).normalize()) + self.assertTrue(lineA.direction() == lineB.direction()) + self.assertFalse(lineA.direction() == lineC.direction()) + + lineA.set(1, 1, 2, 1, 1, 10) + self.assertTrue(lineA.direction() == Vector3d.UNIT_Z) + + lineA.set(1, 5, 1, 1, 1, 1) + self.assertTrue(lineA.direction() == -Vector3d.UNIT_Y) + + lineA.set(1, 1, 1, 7, 1, 1) + self.assertTrue(lineA.direction() == Vector3d.UNIT_X) + + def test_within(self): + line = Line3d(0, 0, 0, 1, 1, 1) + self.assertTrue(line.within(Vector3d(0, 0, 0))) + self.assertTrue(line.within(Vector3d(1, 1, 1))) + self.assertTrue(line.within(Vector3d(0.5, 0.5, 0.5))) + + self.assertFalse(line.within(Vector3d(-0.5, 0.5, 0.5))) + self.assertFalse(line.within(Vector3d(0.5, -0.5, 0.5))) + self.assertFalse(line.within(Vector3d(0.5, 0.5, -0.5))) + + def test_distance(self): + line = Line3d(0, 0, 0, 0, 1, 0) + result = Line3d() + + self.assertTrue(line.distance(Line3d(1, 0.5, 0, -1, 0.5, 0), result)) + self.assertAlmostEqual(result.length(), 0) + self.assertEqual(result, Line3d(0, 0.5, 0, 0, 0.5, 0)) + + self.assertTrue(line.distance(Line3d(1, 0, 0, -1, 0, 0), result)) + self.assertAlmostEqual(result.length(), 0) + self.assertEqual(result, Line3d(0, 0, 0, 0, 0, 0)) + + self.assertTrue(line.distance(Line3d(1, 1.1, 0, -1, 1.1, 0), result)) + self.assertAlmostEqual(result.length(), 0.1, delta=1e-4) + self.assertEqual(result, Line3d(0, 1, 0, 0, 1.1, 0)) + + self.assertTrue(line.distance(Line3d(1, 0.5, 0.4, -1, 0.5, 0.4), + result)) + self.assertAlmostEqual(result.length(), 0.4, delta=1e-4) + self.assertEqual(result, Line3d(0, 0.5, 0, 0, 0.5, 0.4)) + + self.assertTrue(line.distance(Line3d(0, 0.5, 1, 1, 0.5, 0), + result)) + self.assertAlmostEqual(result.length(), math.sin(math.pi / 4), + delta=1e-4) + self.assertEqual(result, Line3d(0, 0.5, 0, 0.5, 0.5, 0.5)) + + # Expect true when lines are parallel + self.assertTrue(line.distance(Line3d(2, 0, 0, 2, 1, 0), result)) + self.assertEqual(result[0], line[0]) + self.assertEqual(result[1], Vector3d(2, 0, 0)) + + self.assertTrue(line.distance(Line3d(2, 1, 0, 2, 0, 0), result)) + self.assertEqual(result[0], line[0]) + self.assertEqual(result[1], Vector3d(2, 0, 0)) + + self.assertTrue(line.distance(Line3d(1, 1, 0, 1, 2, 0), result)) + self.assertEqual(result[0], line[1]) + self.assertEqual(result[1], Vector3d(1, 1, 0)) + + self.assertTrue(line.distance(Line3d(1, 2, 0, 1, 1, 0), result)) + self.assertEqual(result[0], line[1]) + self.assertEqual(result[1], Vector3d(1, 1, 0)) + + # Expect false when the passed in line is a point + self.assertFalse(line.distance(Line3d(2, 0, 0, 2, 0, 0), result)) + + # Expect false when the first line is a point. + line.set(0, 0, 0, 0, 0, 0) + self.assertFalse(line.distance(Line3d(2, 0, 0, 2, 1, 0), result)) + + def test_interesct(self): + line = Line3d(0, 0, 0, 0, 1, 0) + pt = Vector3d() + + self.assertTrue(line.intersect(Line3d(1, 0.5, 0, -1, 0.5, 0))) + self.assertTrue(line.intersect(Line3d(1, 0.5, 0, -1, 0.5, 0), pt)) + self.assertEqual(pt, Vector3d(0, 0.5, 0)) + + self.assertTrue(line.intersect(Line3d(1, 0, 0, -1, 0, 0))) + self.assertTrue(line.intersect(Line3d(1, 0, 0, -1, 0, 0), pt)) + self.assertEqual(pt, Vector3d(0, 0, 0)) + + self.assertTrue(line.intersect(Line3d(1, 1, 0, -1, 1, 0))) + self.assertTrue(line.intersect(Line3d(1, 1, 0, -1, 1, 0), pt)) + self.assertEqual(pt, Vector3d(0, 1, 0)) + + self.assertTrue(line.intersect(Line3d(0, 0.5, -1, 0, 0.5, 1))) + self.assertTrue(line.intersect(Line3d(0, 0.5, -1, 0, 0.5, 1), pt)) + self.assertEqual(pt, Vector3d(0, 0.5, 0)) + + self.assertTrue(line.intersect(Line3d(-1, 0.5, -1, 1, 0.5, 1))) + self.assertTrue(line.intersect(Line3d(-1, 0.5, -1, 1, 0.5, 1), pt)) + self.assertEqual(pt, Vector3d(0, 0.5, 0)) + + self.assertFalse(line.intersect(Line3d(1, 1.1, 0, -1, 1.1, 0))) + self.assertFalse(line.intersect(Line3d(1, -0.1, 0, -1, -0.1, 0))) + + self.assertFalse(line.intersect(Line3d(0.1, 0.1, 0, 0.6, 0.6, 0))) + self.assertFalse(line.intersect(Line3d(-0.1, 0, 0, -0.1, 1, 0))) + + self.assertTrue(line.intersect(Line3d(0, -1, 0, 0, 0.1, 0))) + self.assertTrue(line.intersect(Line3d(0, 1, 0, 0, 1.1, 0))) + + def test_parallel(self): + line = Line3d(0, 0, 0, 0, 1, 0) + self.assertTrue(line.parallel(Line3d(1, 0, 0, 1, 1, 0))) + self.assertTrue(line.parallel(Line3d(1, 1, 0, 1, 0, 0))) + self.assertTrue(line.parallel(Line3d(0, 0, 0, 0, 10, 0))) + self.assertTrue(line.parallel(Line3d(-100, 100, 20, -100, 200, 20))) + + self.assertFalse(line.parallel(Line3d(1, 0, 0, 1, 1, 1))) + self.assertFalse(line.parallel(Line3d(1, 0, 0, 2, 0, 0))) + self.assertFalse(line.parallel(Line3d(1, 0, 1, 2, 0, 1))) + + def test_coplanar(self): + line = Line3d(0, 0, 0, 0, 1, 0) + self.assertTrue(line.coplanar(Line3d(1, 0, 0, 1, 1, 0))) + self.assertTrue(line.coplanar(Line3d(0, 0, 0, 0, 10, 0))) + self.assertTrue(line.coplanar(Line3d(-100, 100, 20, -100, 200, 20))) + + self.assertFalse(line.coplanar(Line3d(1, 0, 0, 1, 1, 1))) + self.assertFalse(line.coplanar(Line3d(1, 0, 1, 2, 0, 0))) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/SignalStats.i b/src/python/SignalStats.i new file mode 100644 index 000000000..9ea9cb7f2 --- /dev/null +++ b/src/python/SignalStats.i @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +%module signalStats + +%{ +#include +%} + +%include "std_string.i" +%include "std_map.i" +%template(map_string_double) std::map; + +namespace ignition +{ + namespace math + { + class SignalMaximum + { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; + public: virtual void Reset(); + public: virtual size_t Count() const; + public: virtual double Value() const; + public: virtual std::string ShortName() const; + public: virtual void InsertData(const double _data); + }; + + class SignalMean + { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; + public: virtual void Reset(); + public: virtual size_t Count() const; + public: virtual double Value() const; + public: virtual std::string ShortName() const; + public: virtual void InsertData(const double _data); + }; + + class SignalMinimum + { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; + public: virtual void Reset(); + public: virtual size_t Count() const; + public: virtual double Value() const; + public: virtual std::string ShortName() const; + public: virtual void InsertData(const double _data); + }; + + class SignalRootMeanSquare + { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; + public: virtual void Reset(); + public: virtual size_t Count() const; + public: virtual double Value() const; + public: virtual std::string ShortName() const; + public: virtual void InsertData(const double _data); + }; + + class SignalMaxAbsoluteValue + { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; + public: virtual void Reset(); + public: virtual size_t Count() const; + public: virtual double Value() const; + public: virtual std::string ShortName() const; + public: virtual void InsertData(const double _data); + }; + + class SignalVariance + { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; + public: virtual void Reset(); + public: virtual size_t Count() const; + public: virtual double Value() const; + public: virtual std::string ShortName() const; + public: virtual void InsertData(const double _data); + }; + + class SignalStats + { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; + public: SignalStats(); + public: ~SignalStats(); + public: SignalStats(const SignalStats &_ss); + public: size_t Count() const; + public: std::map Map() const; + public: void InsertData(const double _data); + public: bool InsertStatistic(const std::string &_name); + public: bool InsertStatistics(const std::string &_names); + public: void Reset(); + }; + + } +} \ No newline at end of file diff --git a/src/python/SignalStats_TEST.py b/src/python/SignalStats_TEST.py new file mode 100644 index 000000000..4451b8c00 --- /dev/null +++ b/src/python/SignalStats_TEST.py @@ -0,0 +1,527 @@ +# Copyright (C) 2021 Open Source Robotics Foundation + +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import math +from ignition.math import Rand +from ignition.math import SignalMaximum +from ignition.math import SignalMinimum +from ignition.math import SignalMean +from ignition.math import SignalVariance +from ignition.math import SignalRootMeanSquare +from ignition.math import SignalMaxAbsoluteValue +from ignition.math import SignalStats + + +class TestSignalStats(unittest.TestCase): + + def test_signal_maximum_constructor(self): + # Constructor + max = SignalMaximum() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + self.assertEqual(max.short_name(), "max") + + # reset + max.reset() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + def test_signal_maximum_constant_values(self): + # Constant values, max should match + max = SignalMaximum() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + max.insert_data(value) + self.assertAlmostEqual(max.value(), value) + self.assertEqual(max.count(), i) + + # reset + max.reset() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + def test_signal_maximum_alternating_values(self): + # Values with alternating sign, increasing magnitude + # Should always match positive value + max = SignalMaximum() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + + for i in range(1, 11): + max.insert_data(value * i) + self.assertAlmostEqual(max.value(), value * i) + max.insert_data(-value * i) + self.assertAlmostEqual(max.value(), value * i) + self.assertEqual(max.count(), i*2) + + # reset + max.reset() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + def test_signal_mean_constructor(self): + # Constructor + mean = SignalMean() + self.assertAlmostEqual(mean.value(), 0.0) + self.assertEqual(mean.count(), 0) + self.assertEqual(mean.short_name(), "mean") + + # reset + mean.reset() + self.assertAlmostEqual(mean.value(), 0.0) + self.assertEqual(mean.count(), 0) + + def test_signal_mean_constant_values(self): + # Constant values, mean should match + mean = SignalMean() + self.assertAlmostEqual(mean.value(), 0.0) + self.assertEqual(mean.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + mean.insert_data(value) + self.assertAlmostEqual(mean.value(), value) + self.assertEqual(mean.count(), i) + + # reset + mean.reset() + self.assertAlmostEqual(mean.value(), 0.0) + self.assertEqual(mean.count(), 0) + + def test_signal_mean_alternating_values(self): + # Values with alternating sign, increasing magnitude + # Should be zero every other time + mean = SignalMean() + self.assertAlmostEqual(mean.value(), 0.0) + self.assertEqual(mean.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + mean.insert_data(value * i) + mean.insert_data(-value * i) + self.assertAlmostEqual(mean.value(), 0.0) + self.assertEqual(mean.count(), i*2) + + # reset + mean.reset() + self.assertAlmostEqual(mean.value(), 0.0) + self.assertEqual(mean.count(), 0) + + def test_signal_minimum_constructor(self): + # Constructor + min = SignalMinimum() + self.assertAlmostEqual(min.value(), 0.0) + self.assertEqual(min.count(), 0) + self.assertEqual(min.short_name(), "min") + + # reset + min.reset() + self.assertAlmostEqual(min.value(), 0.0) + self.assertEqual(min.count(), 0) + + def test_signal_minimum_constant_values(self): + # Constant values, min should match + min = SignalMinimum() + self.assertAlmostEqual(min.value(), 0.0) + self.assertEqual(min.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + min.insert_data(value) + self.assertAlmostEqual(min.value(), value) + self.assertEqual(min.count(), i) + + # reset + min.reset() + self.assertAlmostEqual(min.value(), 0.0) + self.assertEqual(min.count(), 0) + + def test_signal_minimum_alternating_values(self): + # Values with alternating sign, increasing magnitude + # Should always match negative value + min = SignalMinimum() + self.assertAlmostEqual(min.value(), 0.0) + self.assertEqual(min.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + min.insert_data(value * i) + min.insert_data(-value * i) + self.assertAlmostEqual(min.value(), -value * i) + self.assertEqual(min.count(), i*2) + + # reset + min.reset() + self.assertAlmostEqual(min.value(), 0.0) + self.assertEqual(min.count(), 0) + + def test_signal_root_mean_square(self): + # Constructor + rms = SignalRootMeanSquare() + self.assertAlmostEqual(rms.value(), 0.0) + self.assertEqual(rms.count(), 0) + self.assertEqual(rms.short_name(), "rms") + + # reset + rms.reset() + self.assertAlmostEqual(rms.value(), 0.0) + self.assertEqual(rms.count(), 0) + + def test_signal_root_mean_square_constant_values(self): + # Constant values, rms should match + rms = SignalRootMeanSquare() + self.assertAlmostEqual(rms.value(), 0.0) + self.assertEqual(rms.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + rms.insert_data(value) + self.assertAlmostEqual(rms.value(), value) + self.assertEqual(rms.count(), i) + + # reset + rms.reset() + self.assertAlmostEqual(rms.value(), 0.0) + self.assertEqual(rms.count(), 0) + + def test_signal_root_mean_square_alternating_values(self): + # Values with alternating sign, same magnitude + # rms should match absolute value every time + rms = SignalRootMeanSquare() + self.assertAlmostEqual(rms.value(), 0.0) + self.assertEqual(rms.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + rms.insert_data(value) + self.assertAlmostEqual(rms.value(), value) + self.assertEqual(rms.count(), i*2-1) + + rms.insert_data(-value) + self.assertAlmostEqual(rms.value(), value) + self.assertEqual(rms.count(), i*2) + + # reset + rms.reset() + self.assertAlmostEqual(rms.value(), 0.0) + self.assertEqual(rms.count(), 0) + + def test_signal_max_absolute_value_constructor(self): + # Constructor + max = SignalMaxAbsoluteValue() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + self.assertEqual(max.short_name(), "maxAbs") + + # reset + max.reset() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + def test_signal_max_absolute_value_constant_values(self): + # Constant values, max should match + max = SignalMaxAbsoluteValue() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + max.insert_data(value) + self.assertAlmostEqual(max.value(), value) + self.assertEqual(max.count(), i) + + # reset + max.reset() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + def test_signal_max_absolute_value_alternating_values(self): + # Values with alternating sign, increasing magnitude + # max should match absolute value every time + max = SignalMaxAbsoluteValue() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + max.insert_data(value * i) + self.assertAlmostEqual(max.value(), value * i) + self.assertEqual(max.count(), i*2-1) + + max.insert_data(-value * i) + self.assertAlmostEqual(max.value(), value * i) + self.assertEqual(max.count(), i*2) + + # reset + max.reset() + self.assertAlmostEqual(max.value(), 0.0) + self.assertEqual(max.count(), 0) + + def test_signal_variance_constructor(self): + var = SignalVariance() + self.assertAlmostEqual(var.value(), 0.0) + self.assertEqual(var.count(), 0) + self.assertEqual(var.short_name(), "var") + + # reset + var.reset() + self.assertAlmostEqual(var.value(), 0.0) + self.assertEqual(var.count(), 0) + + def test_signal_variance_one_value(self): + # Add one value, expect 0.0 variance + values = {0, 1.0, 10.0, -100.0} + for value in values: + var = SignalVariance() + var.insert_data(value) + self.assertEqual(var.count(), 1) + self.assertAlmostEqual(0.0, var.value()) + + # reset + var.reset() + self.assertAlmostEqual(0.0, var.value()) + self.assertEqual(var.count(), 0) + + def test_signal_variance_constant_values(self): + # Constant values, expect 0.0 variance + var = SignalVariance() + value = 3.14159 + + # Loop two times to verify reset + for j in range(2): + for i in range(1, 11): + var.insert_data(value) + self.assertAlmostEqual(0.0, var.value()) + self.assertEqual(var.count(), i) + + # reset + var.reset() + self.assertAlmostEqual(var.value(), 0.0) + self.assertEqual(var.count(), 0) + + def test_signal_variance_random_values(self): + # Random normally distributed values + # The sample variance has the following variance: + # 2 variance^2 / (count - 1) + # en.wikipedia.org/wiki/Variance#Distribution_of_the_sample_variance + # We will use 5 sigma (4e-5 chance of failure) + var = SignalVariance() + stdDev = 3.14159 + count = 10000 + sigma = 5.0 + for i in range(count): + var.insert_data(Rand.dbl_normal(0.0, stdDev)) + + variance = stdDev*stdDev + sampleVariance2 = 2 * variance*variance / (count - 1) + self.assertAlmostEqual(var.value(), variance, + delta=sigma*math.sqrt(sampleVariance2)) + + # reset + var.reset() + self.assertAlmostEqual(var.value(), 0.0) + self.assertEqual(var.count(), 0) + + def test_signal_stats_constructor(self): + # Constructor + stats = SignalStats() + self.assertTrue(stats.map().empty()) + self.assertEqual(stats.count(), 0) + + stats2 = SignalStats(stats) + self.assertEqual(stats.count(), stats2.count()) + + # reset + stats.reset() + self.assertTrue(stats.map().empty()) + self.assertEqual(stats.count(), 0) + + def test_01_signal_stats_intern_statistic(self): + # insert static + stats = SignalStats() + self.assertTrue(stats.map().empty()) + + self.assertTrue(stats.insert_statistic("max")) + self.assertFalse(stats.insert_statistic("max")) + self.assertFalse(stats.map().empty()) + + self.assertTrue(stats.insert_statistic("maxAbs")) + self.assertFalse(stats.insert_statistic("maxAbs")) + self.assertFalse(stats.map().empty()) + + self.assertTrue(stats.insert_statistic("mean")) + self.assertFalse(stats.insert_statistic("mean")) + self.assertFalse(stats.map().empty()) + + self.assertTrue(stats.insert_statistic("min")) + self.assertFalse(stats.insert_statistic("min")) + self.assertFalse(stats.map().empty()) + + self.assertTrue(stats.insert_statistic("rms")) + self.assertFalse(stats.insert_statistic("rms")) + self.assertFalse(stats.map().empty()) + + self.assertTrue(stats.insert_statistic("var")) + self.assertFalse(stats.insert_statistic("var")) + self.assertFalse(stats.map().empty()) + + self.assertFalse(stats.insert_statistic("FakeStatistic")) + + # map with no data + map = stats.map() + self.assertFalse(map.empty()) + self.assertEqual(map.size(), 6) + self.assertEqual(map.count("max"), 1) + self.assertEqual(map.count("maxAbs"), 1) + self.assertEqual(map.count("mean"), 1) + self.assertEqual(map.count("min"), 1) + self.assertEqual(map.count("rms"), 1) + self.assertEqual(map.count("var"), 1) + self.assertEqual(map.count("FakeStatistic"), 0) + + stats2 = SignalStats(stats) + map2 = stats2.map() + self.assertFalse(map2.empty()) + self.assertEqual(map.size(), map2.size()) + self.assertEqual(map.count("max"), map2.count("max")) + self.assertEqual(map.count("maxAbs"), map2.count("maxAbs")) + self.assertEqual(map.count("mean"), map2.count("mean")) + self.assertEqual(map.count("min"), map2.count("min")) + self.assertEqual(map.count("rms"), map2.count("rms")) + self.assertEqual(map.count("var"), map2.count("var")) + self.assertEqual(map.count("FakeStatistic"), + map2.count("FakeStatistic")) + + def test_02_signal_stats_intern_statistic(self): + # insert statics + stats = SignalStats() + self.assertFalse(stats.insert_statistics("")) + self.assertTrue(stats.map().empty()) + + self.assertTrue(stats.insert_statistics("maxAbs,rms")) + self.assertEqual(stats.map().size(), 2) + self.assertFalse(stats.insert_statistics("maxAbs,rms")) + self.assertFalse(stats.insert_statistics("maxAbs")) + self.assertFalse(stats.insert_statistics("rms")) + self.assertEqual(stats.map().size(), 2) + + self.assertFalse(stats.insert_statistics("mean,FakeStatistic")) + self.assertEqual(stats.map().size(), 3) + + self.assertFalse(stats.insert_statistics("var,FakeStatistic")) + self.assertEqual(stats.map().size(), 4) + + self.assertFalse(stats.insert_statistics("max,FakeStatistic")) + self.assertEqual(stats.map().size(), 5) + + self.assertFalse(stats.insert_statistics("min,FakeStatistic")) + self.assertEqual(stats.map().size(), 6) + + self.assertFalse(stats.insert_statistics("FakeStatistic")) + self.assertEqual(stats.map().size(), 6) + + # map with no data + map = stats.map() + self.assertFalse(map.empty()) + self.assertEqual(map.size(), 6) + self.assertEqual(map.count("max"), 1) + self.assertEqual(map.count("maxAbs"), 1) + self.assertEqual(map.count("mean"), 1) + self.assertEqual(map.count("min"), 1) + self.assertEqual(map.count("rms"), 1) + self.assertEqual(map.count("var"), 1) + self.assertEqual(map.count("FakeStatistic"), 0) + + def test_signal_stats_alternating_values(self): + # Add some statistics + stats = SignalStats() + self.assertTrue(stats.insert_statistics("max,maxAbs,mean,min,rms")) + self.assertEqual(stats.map().size(), 5) + + # No data yet + self.assertEqual(stats.count(), 0) + + # Insert data with alternating signs + value = 3.14159 + stats.insert_data(value) + stats.insert_data(-value) + self.assertEqual(stats.count(), 2) + + map = stats.map() + self.assertAlmostEqual(map["max"], value) + self.assertAlmostEqual(map["maxAbs"], value) + self.assertAlmostEqual(map["min"], -value) + self.assertAlmostEqual(map["rms"], value) + self.assertAlmostEqual(map["mean"], 0.0) + + # test operator= + copy = SignalStats(stats) + self.assertEqual(copy.count(), 2) + map = stats.map() + self.assertEqual(map.size(), 5) + self.assertAlmostEqual(map["max"], value) + self.assertAlmostEqual(map["maxAbs"], value) + self.assertAlmostEqual(map["min"], -value) + self.assertAlmostEqual(map["rms"], value) + self.assertAlmostEqual(map["mean"], 0.0) + + stats.reset() + self.assertEqual(stats.map().size(), 5) + self.assertEqual(stats.count(), 0) + map = stats.map() + self.assertAlmostEqual(map["max"], 0.0) + self.assertAlmostEqual(map["maxAbs"], 0.0) + self.assertAlmostEqual(map["min"], 0.0) + self.assertAlmostEqual(map["rms"], 0.0) + self.assertAlmostEqual(map["mean"], 0.0) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/Temperature.i b/src/python/Temperature.i similarity index 97% rename from src/Temperature.i rename to src/python/Temperature.i index bf1926245..ba6e6426e 100644 --- a/src/Temperature.i +++ b/src/python/Temperature.i @@ -27,7 +27,8 @@ namespace ignition namespace math { class Temperature - { + { + %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: Temperature(); public: Temperature(const double _temp); public: Temperature(const Temperature &_temp); diff --git a/src/Temperature_TEST.py b/src/python/Temperature_TEST.py similarity index 55% rename from src/Temperature_TEST.py rename to src/python/Temperature_TEST.py index f80233559..3fc2e02a1 100644 --- a/src/Temperature_TEST.py +++ b/src/python/Temperature_TEST.py @@ -20,14 +20,14 @@ class TestTemperature(unittest.TestCase): def test_temperatur_constructor(self): temp = Temperature() - self.assertAlmostEqual(temp.Kelvin(), 0.0, 1e-6) + self.assertAlmostEqual(temp.kelvin(), 0.0, 1e-6) temp2 = Temperature(1.1) - self.assertAlmostEqual(temp2.Kelvin(), 1.1, 1e-6) + self.assertAlmostEqual(temp2.kelvin(), 1.1, 1e-6) temp3 = Temperature(temp2) - self.assertAlmostEqual(temp3.Kelvin(), 1.1, delta=1e-6) - self.assertAlmostEqual(temp3.Celsius(), -272.05, delta=1e-6) + self.assertAlmostEqual(temp3.kelvin(), 1.1, delta=1e-6) + self.assertAlmostEqual(temp3.celsius(), -272.05, delta=1e-6) self.assertTrue(temp2 == temp3) self.assertTrue(temp2 == 1.1) @@ -47,31 +47,31 @@ def test_temperatur_constructor(self): self.assertTrue(temp >= 0.0) def test_temperatur_conversions(self): - self.assertAlmostEqual(Temperature.KelvinToCelsius(0), -273.15, + self.assertAlmostEqual(Temperature.kelvin_to_celsius(0), -273.15, delta=1e-6) - self.assertAlmostEqual(Temperature.KelvinToFahrenheit(300), 80.33, + self.assertAlmostEqual(Temperature.kelvin_to_fahrenheit(300), 80.33, delta=1e-6) - self.assertAlmostEqual(Temperature.CelsiusToFahrenheit(20.0), 68.0, + self.assertAlmostEqual(Temperature.celsius_to_fahrenheit(20.0), 68.0, delta=1e-6) - self.assertAlmostEqual(Temperature.CelsiusToKelvin(10.0), 283.15, + self.assertAlmostEqual(Temperature.celsius_to_kelvin(10.0), 283.15, delta=1e-6) - self.assertAlmostEqual(Temperature.FahrenheitToCelsius(-40.0), - Temperature.CelsiusToFahrenheit(-40.0), 1e-6) - self.assertAlmostEqual(Temperature.FahrenheitToKelvin(60.0), 288.7055, - delta=1e-3) + self.assertAlmostEqual(Temperature.fahrenheit_to_celsius(-40.0), + Temperature.celsius_to_fahrenheit(-40.0), 1e-6) + self.assertAlmostEqual(Temperature.fahrenheit_to_kelvin(60.0), + 288.7055, delta=1e-3) def test_temperatur_mutators_accessors(self): temp = Temperature() - self.assertAlmostEqual(temp.Kelvin(), 0.0, delta=1e-6) + self.assertAlmostEqual(temp.kelvin(), 0.0, delta=1e-6) - temp.SetKelvin(10) - self.assertAlmostEqual(temp.Kelvin(), 10.0, delta=1e-6) + temp.set_kelvin(10) + self.assertAlmostEqual(temp.kelvin(), 10.0, delta=1e-6) - temp.SetCelsius(20) + temp.set_celsius(20) self.assertAlmostEqual(temp(), 293.15, delta=1e-6) - temp.SetFahrenheit(30) - self.assertAlmostEqual(temp.Fahrenheit(), 30.0, delta=1e-6) + temp.set_fahrenheit(30) + self.assertAlmostEqual(temp.fahrenheit(), 30.0, delta=1e-6) self.assertAlmostEqual(temp(), 272.0388889, delta=1e-6) def test_temperatur_operators(self): @@ -85,37 +85,37 @@ def test_temperatur_operators(self): temp2 = Temperature(temp) self.assertTrue(temp == temp2) - self.assertAlmostEqual((temp + temp2).Kelvin(), 60, delta=1e-6) - self.assertAlmostEqual((temp + 40).Kelvin(), 70, delta=1e-6) + self.assertAlmostEqual((temp + temp2).kelvin(), 60, delta=1e-6) + self.assertAlmostEqual((temp + 40).kelvin(), 70, delta=1e-6) - self.assertAlmostEqual((temp - temp2).Kelvin(), 0, delta=1e-6) - self.assertAlmostEqual((temp - 20).Kelvin(), 10.0, delta=1e-6) + self.assertAlmostEqual((temp - temp2).kelvin(), 0, delta=1e-6) + self.assertAlmostEqual((temp - 20).kelvin(), 10.0, delta=1e-6) - self.assertAlmostEqual((temp * temp2).Kelvin(), 900, delta=1e-6) - self.assertAlmostEqual((temp * 2).Kelvin(), 60.0, delta=1e-6) + self.assertAlmostEqual((temp * temp2).kelvin(), 900, delta=1e-6) + self.assertAlmostEqual((temp * 2).kelvin(), 60.0, delta=1e-6) - self.assertAlmostEqual((temp / temp2).Kelvin(), 1.0, delta=1e-6) - self.assertAlmostEqual((temp / 2).Kelvin(), 15.0, delta=1e-6) + self.assertAlmostEqual((temp / temp2).kelvin(), 1.0, delta=1e-6) + self.assertAlmostEqual((temp / 2).kelvin(), 15.0, delta=1e-6) temp += temp2 - self.assertAlmostEqual(temp.Kelvin(), 60.0, delta=1e-6) + self.assertAlmostEqual(temp.kelvin(), 60.0, delta=1e-6) temp -= temp2 - self.assertAlmostEqual(temp.Kelvin(), 30.0, delta=1e-6) + self.assertAlmostEqual(temp.kelvin(), 30.0, delta=1e-6) temp += 5.0 - self.assertAlmostEqual(temp.Kelvin(), 35.0, delta=1e-6) + self.assertAlmostEqual(temp.kelvin(), 35.0, delta=1e-6) temp -= 5.0 - self.assertAlmostEqual(temp.Kelvin(), 30.0, delta=1e-6) + self.assertAlmostEqual(temp.kelvin(), 30.0, delta=1e-6) temp *= temp2 - self.assertAlmostEqual(temp.Kelvin(), 900, delta=1e-6) + self.assertAlmostEqual(temp.kelvin(), 900, delta=1e-6) temp /= temp2 - self.assertAlmostEqual(temp.Kelvin(), 30, delta=1e-6) + self.assertAlmostEqual(temp.kelvin(), 30, delta=1e-6) temp *= 4.0 - self.assertAlmostEqual(temp.Kelvin(), 120, delta=1e-6) + self.assertAlmostEqual(temp.kelvin(), 120, delta=1e-6) temp /= 4.0 - self.assertAlmostEqual(temp.Kelvin(), 30, delta=1e-6) + self.assertAlmostEqual(temp.kelvin(), 30, delta=1e-6) temp3 = Temperature(temp) self.assertTrue(temp3 == temp) diff --git a/src/python/python.i b/src/python/python.i index cad4e03ac..4aab42ac7 100644 --- a/src/python/python.i +++ b/src/python/python.i @@ -5,7 +5,7 @@ %include Vector2.i %include Vector3.i %include Vector4.i -%include Temperature.i %include Line2.i %include Line3.i %include SignalStats.i +%include Temperature.i From 34be3d6b36ebe0ef7b9f62173bd92d53d99b027e Mon Sep 17 00:00:00 2001 From: Marcos Wagner Date: Wed, 25 Aug 2021 10:52:04 -0300 Subject: [PATCH 4/5] Applying alphasort to include and snake_case to variables Signed-off-by: Marcos Wagner --- src/python/Line2.i | 2 +- src/python/Line2_TEST.py | 200 ++++++++++++++++----------------- src/python/Line3.i | 2 +- src/python/Line3_TEST.py | 172 ++++++++++++++-------------- src/python/SignalStats.i | 2 +- src/python/SignalStats_TEST.py | 14 +-- 6 files changed, 196 insertions(+), 196 deletions(-) diff --git a/src/python/Line2.i b/src/python/Line2.i index ec27c9a7b..0e8e93c84 100644 --- a/src/python/Line2.i +++ b/src/python/Line2.i @@ -18,8 +18,8 @@ %module line2 %{ #include -#include #include +#include %} %include "std_string.i" diff --git a/src/python/Line2_TEST.py b/src/python/Line2_TEST.py index 6ec549dc0..0c05a2e74 100644 --- a/src/python/Line2_TEST.py +++ b/src/python/Line2_TEST.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest import math +import unittest from ignition.math import Line2d from ignition.math import Vector2d @@ -21,23 +21,23 @@ class TestLine2d(unittest.TestCase): def test_construction(self): - lineA = Line2d(0, 0, 10, 10) - self.assertAlmostEqual(lineA[0].x(), 0.0) - self.assertAlmostEqual(lineA[0].y(), 0.0) - self.assertAlmostEqual(lineA[1].x(), 10.0) - self.assertAlmostEqual(lineA[1].y(), 10.0) + line_a = Line2d(0, 0, 10, 10) + self.assertAlmostEqual(line_a[0].x(), 0.0) + self.assertAlmostEqual(line_a[0].y(), 0.0) + self.assertAlmostEqual(line_a[1].x(), 10.0) + self.assertAlmostEqual(line_a[1].y(), 10.0) - lineB = Line2d(Vector2d(1, 2), Vector2d(3, 4)) - self.assertAlmostEqual(lineB[0].x(), 1.0) - self.assertAlmostEqual(lineB[0].y(), 2.0) - self.assertAlmostEqual(lineB[1].x(), 3.0) - self.assertAlmostEqual(lineB[1].y(), 4.0) + line_b = Line2d(Vector2d(1, 2), Vector2d(3, 4)) + self.assertAlmostEqual(line_b[0].x(), 1.0) + self.assertAlmostEqual(line_b[0].y(), 2.0) + self.assertAlmostEqual(line_b[1].x(), 3.0) + self.assertAlmostEqual(line_b[1].y(), 4.0) - self.assertAlmostEqual(lineB[2].x(), lineB[1].x()) + self.assertAlmostEqual(line_b[2].x(), line_b[1].x()) def test_length(self): - lineA = Line2d(0, 0, 10, 10) - self.assertAlmostEqual(lineA.length(), math.sqrt(200), delta=1e-10) + line_a = Line2d(0, 0, 10, 10) + self.assertAlmostEqual(line_a.length(), math.sqrt(200), delta=1e-10) def test_slope(self): line = Line2d(0, 0, 10, 10) @@ -59,152 +59,152 @@ def test_parallel_line(self): line = Line2d(0, 0, 0, 0) self.assertTrue(line.parallel(line, 1e-10)) - lineA = Line2d(0, 0, 10, 0) - lineB = Line2d(0, 0, 10, 0) - self.assertTrue(lineA.parallel(lineB, 1e-10)) + line_a = Line2d(0, 0, 10, 0) + line_b = Line2d(0, 0, 10, 0) + self.assertTrue(line_a.parallel(line_b, 1e-10)) - lineB.set(0, 0, 0, 10) - self.assertFalse(lineA.parallel(lineB)) + line_b.set(0, 0, 0, 10) + self.assertFalse(line_a.parallel(line_b)) - lineB.set(0, 10, 10, 10) - self.assertTrue(lineA.parallel(lineB)) + line_b.set(0, 10, 10, 10) + self.assertTrue(line_a.parallel(line_b)) - lineB.set(0, 10, 10, 10.00001) - self.assertFalse(lineA.parallel(lineB, 1e-10)) - self.assertFalse(lineA.parallel(lineB)) - self.assertTrue(lineA.parallel(lineB, 1e-3)) + line_b.set(0, 10, 10, 10.00001) + self.assertFalse(line_a.parallel(line_b, 1e-10)) + self.assertFalse(line_a.parallel(line_b)) + self.assertTrue(line_a.parallel(line_b, 1e-3)) def test_collinear_line(self): # Line is always collinear with itself line = Line2d(0, 0, 10, 0) self.assertTrue(line.collinear(line, 1e-10)) - lineA = Line2d(0, 0, 10, 0) - lineB = Line2d(0, 0, 10, 0) - self.assertTrue(lineA.collinear(lineB, 1e-10)) + line_a = Line2d(0, 0, 10, 0) + line_b = Line2d(0, 0, 10, 0) + self.assertTrue(line_a.collinear(line_b, 1e-10)) - lineB.set(0, 10, 10, 10) - self.assertFalse(lineA.collinear(lineB)) + line_b.set(0, 10, 10, 10) + self.assertFalse(line_a.collinear(line_b)) - lineB.set(9, 0, 10, 0.00001) - self.assertFalse(lineA.collinear(lineB, 1e-10)) - self.assertFalse(lineA.collinear(lineB)) - self.assertTrue(lineA.collinear(lineB, 1e-3)) + line_b.set(9, 0, 10, 0.00001) + self.assertFalse(line_a.collinear(line_b, 1e-10)) + self.assertFalse(line_a.collinear(line_b)) + self.assertTrue(line_a.collinear(line_b, 1e-3)) def test_collinear_point(self): - lineA = Line2d(0, 0, 10, 0) + line_a = Line2d(0, 0, 10, 0) pt = Vector2d(0, 0) - self.assertTrue(lineA.collinear(pt)) + self.assertTrue(line_a.collinear(pt)) - ptLine = Line2d(pt, pt) - self.assertTrue(lineA.collinear(ptLine)) + pt_line = Line2d(pt, pt) + self.assertTrue(line_a.collinear(pt_line)) pt.set(1000, 0) - self.assertTrue(lineA.collinear(pt, 1e-10)) + self.assertTrue(line_a.collinear(pt, 1e-10)) - ptLine = Line2d(pt, pt) - self.assertTrue(lineA.parallel(ptLine)) - self.assertFalse(lineA.intersect(ptLine)) - self.assertFalse(lineA.collinear(ptLine, 1e-10)) + pt_line = Line2d(pt, pt) + self.assertTrue(line_a.parallel(pt_line)) + self.assertFalse(line_a.intersect(pt_line)) + self.assertFalse(line_a.collinear(pt_line, 1e-10)) pt.set(10, 0) - ptLine.set(pt, pt) - self.assertTrue(lineA.collinear(ptLine, 1e-10)) + pt_line.set(pt, pt) + self.assertTrue(line_a.collinear(pt_line, 1e-10)) pt.set(0, 0.00001) - self.assertFalse(lineA.collinear(pt)) - self.assertTrue(lineA.collinear(pt, 1e-3)) + self.assertFalse(line_a.collinear(pt)) + self.assertTrue(line_a.collinear(pt, 1e-3)) - ptLine = Line2d(pt, pt) - self.assertFalse(lineA.collinear(ptLine)) - self.assertTrue(lineA.parallel(ptLine)) - self.assertFalse(lineA.intersect(ptLine)) - self.assertTrue(lineA.intersect(ptLine, 1e-2)) - self.assertTrue(lineA.collinear(ptLine, 1e-3)) + pt_line = Line2d(pt, pt) + self.assertFalse(line_a.collinear(pt_line)) + self.assertTrue(line_a.parallel(pt_line)) + self.assertFalse(line_a.intersect(pt_line)) + self.assertTrue(line_a.intersect(pt_line, 1e-2)) + self.assertTrue(line_a.collinear(pt_line, 1e-3)) pt.set(0, -0.00001) - self.assertFalse(lineA.collinear(pt)) - self.assertTrue(lineA.collinear(pt, 1e-3)) + self.assertFalse(line_a.collinear(pt)) + self.assertTrue(line_a.collinear(pt, 1e-3)) - ptLine = Line2d(pt, pt) - self.assertFalse(lineA.collinear(ptLine)) - self.assertTrue(lineA.collinear(ptLine, 1e-4)) + pt_line = Line2d(pt, pt) + self.assertFalse(line_a.collinear(pt_line)) + self.assertTrue(line_a.collinear(pt_line, 1e-4)) def test_intersect(self): pt = Vector2d() # parallel horizontal lines - lineA = Line2d(1, 1, 2, 1) - lineB = Line2d(1, 2, 2, 2) - self.assertFalse(lineA.intersect(lineB, pt)) + line_a = Line2d(1, 1, 2, 1) + line_b = Line2d(1, 2, 2, 2) + self.assertFalse(line_a.intersect(line_b, pt)) # parallel vertical lines - lineA.set(1, 1, 1, 10) - lineB.set(2, 1, 2, 10) - self.assertFalse(lineA.intersect(lineB, pt)) + line_a.set(1, 1, 1, 10) + line_b.set(2, 1, 2, 10) + self.assertFalse(line_a.intersect(line_b, pt)) # Two lines that form an inverted T with a gap - lineA.set(1, 1, 1, 10) - lineB.set(0, 0, 2, 0) - self.assertFalse(lineA.intersect(lineB, pt)) + line_a.set(1, 1, 1, 10) + line_b.set(0, 0, 2, 0) + self.assertFalse(line_a.intersect(line_b, pt)) # Two lines that form a T with a gap - lineA.set(1, 1, 1, 10) - lineB.set(0, 10.1, 2, 10.1) - self.assertFalse(lineA.intersect(lineB, pt)) + line_a.set(1, 1, 1, 10) + line_b.set(0, 10.1, 2, 10.1) + self.assertFalse(line_a.intersect(line_b, pt)) # Two lines that form an inverted T with a gap - lineA.set(0, -10, 0, 10) - lineB.set(1, 0, 10, 0) - self.assertFalse(lineA.intersect(lineB, pt)) + line_a.set(0, -10, 0, 10) + line_b.set(1, 0, 10, 0) + self.assertFalse(line_a.intersect(line_b, pt)) # Two lines that form a T with a gap - lineA.set(0, -10, 0, 10) - lineB.set(-1, 0, -10, 0) - self.assertFalse(lineA.intersect(lineB, pt)) + line_a.set(0, -10, 0, 10) + line_b.set(-1, 0, -10, 0) + self.assertFalse(line_a.intersect(line_b, pt)) # Two collinear lines, one starts where the other stopped - lineA.set(1, 1, 1, 10) - lineB.set(1, 10, 1, 11) - self.assertTrue(lineA.intersect(lineB, pt)) + line_a.set(1, 1, 1, 10) + line_b.set(1, 10, 1, 11) + self.assertTrue(line_a.intersect(line_b, pt)) self.assertEqual(pt, Vector2d(1, 10)) # Two collinear lines, one overlaps the other - lineA.set(0, 0, 0, 10) - lineB.set(0, 9, 0, 11) - self.assertTrue(lineA.intersect(lineB, pt)) + line_a.set(0, 0, 0, 10) + line_b.set(0, 9, 0, 11) + self.assertTrue(line_a.intersect(line_b, pt)) self.assertEqual(pt, Vector2d(0, 9)) # Two collinear lines, one overlaps the other - lineA.set(0, 0, 0, 10) - lineB.set(0, -10, 0, 1) - self.assertTrue(lineA.intersect(lineB, pt)) + line_a.set(0, 0, 0, 10) + line_b.set(0, -10, 0, 1) + self.assertTrue(line_a.intersect(line_b, pt)) self.assertEqual(pt, Vector2d(0, 1)) # Two intersecting lines - lineA.set(0, 0, 10, 10) - lineB.set(0, 10, 10, 0) - self.assertTrue(lineA.intersect(lineB, pt)) + line_a.set(0, 0, 10, 10) + line_b.set(0, 10, 10, 0) + self.assertTrue(line_a.intersect(line_b, pt)) self.assertEqual(pt, Vector2d(5, 5)) def test_equality(self): - lineA = Line2d(1, 1, 2, 1) - lineB = Line2d(1, 2, 2, 2) + line_a = Line2d(1, 1, 2, 1) + line_b = Line2d(1, 2, 2, 2) - self.assertTrue(lineA != lineB) - self.assertTrue(lineA == lineA) + self.assertTrue(line_a != line_b) + self.assertTrue(line_a == line_a) - lineB.set(1, 1, 2, 1.1) - self.assertFalse(lineA == lineB) + line_b.set(1, 1, 2, 1.1) + self.assertFalse(line_a == line_b) - lineB.set(1, 1, 2.1, 1) - self.assertFalse(lineA == lineB) + line_b.set(1, 1, 2.1, 1) + self.assertFalse(line_a == line_b) - lineB.set(1, 1.1, 2, 1) - self.assertFalse(lineA == lineB) + line_b.set(1, 1.1, 2, 1) + self.assertFalse(line_a == line_b) - lineB.set(1.1, 1, 2, 1) - self.assertFalse(lineA == lineB) + line_b.set(1.1, 1, 2, 1) + self.assertFalse(line_a == line_b) def test_serialization(self): line = Line2d(0, 1, 2, 3) diff --git a/src/python/Line3.i b/src/python/Line3.i index 2e3b2c3d2..476abfced 100644 --- a/src/python/Line3.i +++ b/src/python/Line3.i @@ -18,8 +18,8 @@ %module line3 %{ #include -#include #include +#include %} %include "std_string.i" diff --git a/src/python/Line3_TEST.py b/src/python/Line3_TEST.py index eff3eed2c..9d8a61d41 100644 --- a/src/python/Line3_TEST.py +++ b/src/python/Line3_TEST.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest import math +import unittest from ignition.math import Line3d from ignition.math import Vector3d @@ -21,114 +21,114 @@ class TestLine3d(unittest.TestCase): def test_construction(self): - lineA = Line3d(0, 0, 10, 10) - self.assertAlmostEqual(lineA[0].x(), 0.0) - self.assertAlmostEqual(lineA[0].y(), 0.0) - self.assertAlmostEqual(lineA[0].z(), 0.0) - self.assertAlmostEqual(lineA[1].x(), 10.0) - self.assertAlmostEqual(lineA[1].y(), 10.0) - self.assertAlmostEqual(lineA[1].z(), 0.0) - lineB = Line3d(Vector3d(1, 2, 3), Vector3d(4, 5, 6)) - self.assertAlmostEqual(lineB[0].x(), 1.0) - self.assertAlmostEqual(lineB[0].y(), 2.0) - self.assertAlmostEqual(lineB[0].z(), 3.0) - self.assertAlmostEqual(lineB[1].x(), 4.0) - self.assertAlmostEqual(lineB[1].y(), 5.0) - self.assertAlmostEqual(lineB[1].z(), 6.0) - - lineC = Line3d(0, 0, 5, 10, 10, 6) - self.assertAlmostEqual(lineC[0].x(), 0.0) - self.assertAlmostEqual(lineC[0].y(), 0.0) - self.assertAlmostEqual(lineC[0].z(), 5.0) - self.assertAlmostEqual(lineC[1].x(), 10.0) - self.assertAlmostEqual(lineC[1].y(), 10.0) - self.assertAlmostEqual(lineC[1].z(), 6.0) - - self.assertAlmostEqual(lineB[2].x(), lineB[1].x()) + line_a = Line3d(0, 0, 10, 10) + self.assertAlmostEqual(line_a[0].x(), 0.0) + self.assertAlmostEqual(line_a[0].y(), 0.0) + self.assertAlmostEqual(line_a[0].z(), 0.0) + self.assertAlmostEqual(line_a[1].x(), 10.0) + self.assertAlmostEqual(line_a[1].y(), 10.0) + self.assertAlmostEqual(line_a[1].z(), 0.0) + line_b = Line3d(Vector3d(1, 2, 3), Vector3d(4, 5, 6)) + self.assertAlmostEqual(line_b[0].x(), 1.0) + self.assertAlmostEqual(line_b[0].y(), 2.0) + self.assertAlmostEqual(line_b[0].z(), 3.0) + self.assertAlmostEqual(line_b[1].x(), 4.0) + self.assertAlmostEqual(line_b[1].y(), 5.0) + self.assertAlmostEqual(line_b[1].z(), 6.0) + + line_c = Line3d(0, 0, 5, 10, 10, 6) + self.assertAlmostEqual(line_c[0].x(), 0.0) + self.assertAlmostEqual(line_c[0].y(), 0.0) + self.assertAlmostEqual(line_c[0].z(), 5.0) + self.assertAlmostEqual(line_c[1].x(), 10.0) + self.assertAlmostEqual(line_c[1].y(), 10.0) + self.assertAlmostEqual(line_c[1].z(), 6.0) + + self.assertAlmostEqual(line_b[2].x(), line_b[1].x()) def test_set(self): - lineA = Line3d() - lineA.set(1, 1, 2, 2) - self.assertAlmostEqual(lineA[0].x(), 1.0) - self.assertAlmostEqual(lineA[0].y(), 1.0) - self.assertAlmostEqual(lineA[0].z(), 0.0) - self.assertAlmostEqual(lineA[1].x(), 2.0) - self.assertAlmostEqual(lineA[1].y(), 2.0) - self.assertAlmostEqual(lineA[1].z(), 0.0) - - lineA.set(10, 11, 12, 13, 14, 15) - self.assertAlmostEqual(lineA[0].x(), 10.0) - self.assertAlmostEqual(lineA[0].y(), 11.0) - self.assertAlmostEqual(lineA[0].z(), 12.0) - self.assertAlmostEqual(lineA[1].x(), 13.0) - self.assertAlmostEqual(lineA[1].y(), 14.0) - self.assertAlmostEqual(lineA[1].z(), 15.0) - - lineA.set_a(Vector3d(0, -1, -2)) - self.assertAlmostEqual(lineA[0].x(), 0.0) - self.assertAlmostEqual(lineA[0].y(), -1.0) - self.assertAlmostEqual(lineA[0].z(), -2.0) - self.assertAlmostEqual(lineA[1].x(), 13.0) - self.assertAlmostEqual(lineA[1].y(), 14.0) - self.assertAlmostEqual(lineA[1].z(), 15.0) - - lineA.set_b(Vector3d(5, 6, 7)) - self.assertAlmostEqual(lineA[0].x(), 0.0) - self.assertAlmostEqual(lineA[0].y(), -1.0) - self.assertAlmostEqual(lineA[0].z(), -2.0) - self.assertAlmostEqual(lineA[1].x(), 5.0) - self.assertAlmostEqual(lineA[1].y(), 6.0) - self.assertAlmostEqual(lineA[1].z(), 7.0) + line_a = Line3d() + line_a.set(1, 1, 2, 2) + self.assertAlmostEqual(line_a[0].x(), 1.0) + self.assertAlmostEqual(line_a[0].y(), 1.0) + self.assertAlmostEqual(line_a[0].z(), 0.0) + self.assertAlmostEqual(line_a[1].x(), 2.0) + self.assertAlmostEqual(line_a[1].y(), 2.0) + self.assertAlmostEqual(line_a[1].z(), 0.0) + + line_a.set(10, 11, 12, 13, 14, 15) + self.assertAlmostEqual(line_a[0].x(), 10.0) + self.assertAlmostEqual(line_a[0].y(), 11.0) + self.assertAlmostEqual(line_a[0].z(), 12.0) + self.assertAlmostEqual(line_a[1].x(), 13.0) + self.assertAlmostEqual(line_a[1].y(), 14.0) + self.assertAlmostEqual(line_a[1].z(), 15.0) + + line_a.set_a(Vector3d(0, -1, -2)) + self.assertAlmostEqual(line_a[0].x(), 0.0) + self.assertAlmostEqual(line_a[0].y(), -1.0) + self.assertAlmostEqual(line_a[0].z(), -2.0) + self.assertAlmostEqual(line_a[1].x(), 13.0) + self.assertAlmostEqual(line_a[1].y(), 14.0) + self.assertAlmostEqual(line_a[1].z(), 15.0) + + line_a.set_b(Vector3d(5, 6, 7)) + self.assertAlmostEqual(line_a[0].x(), 0.0) + self.assertAlmostEqual(line_a[0].y(), -1.0) + self.assertAlmostEqual(line_a[0].z(), -2.0) + self.assertAlmostEqual(line_a[1].x(), 5.0) + self.assertAlmostEqual(line_a[1].y(), 6.0) + self.assertAlmostEqual(line_a[1].z(), 7.0) def test_length(self): - lineA = Line3d(0, 0, 0, 10, 10, 10) - self.assertAlmostEqual(lineA.length(), math.sqrt(300), delta=1e-10) + line_a = Line3d(0, 0, 0, 10, 10, 10) + self.assertAlmostEqual(line_a.length(), math.sqrt(300), delta=1e-10) def test_equality(self): - lineA = Line3d(1, 1, 1, 2, 1, 2) - lineB = Line3d(1, 2, 3, 2, 2, 4) + line_a = Line3d(1, 1, 1, 2, 1, 2) + line_b = Line3d(1, 2, 3, 2, 2, 4) - self.assertTrue(lineA != lineB) - self.assertTrue(lineA == lineA) + self.assertTrue(line_a != line_b) + self.assertTrue(line_a == line_a) - lineB.set(1, 1, 1, 2, 1.1, 2) - self.assertFalse(lineA == lineB) + line_b.set(1, 1, 1, 2, 1.1, 2) + self.assertFalse(line_a == line_b) - lineB.set(1, 1, 1, 2.1, 1, 2) - self.assertFalse(lineA == lineB) + line_b.set(1, 1, 1, 2.1, 1, 2) + self.assertFalse(line_a == line_b) - lineB.set(1, 1, 1.1, 2, 1, 2) - self.assertFalse(lineA == lineB) + line_b.set(1, 1, 1.1, 2, 1, 2) + self.assertFalse(line_a == line_b) - lineB.set(1.1, 1, 1, 2, 1, 2) - self.assertFalse(lineA == lineB) + line_b.set(1.1, 1, 1, 2, 1, 2) + self.assertFalse(line_a == line_b) def test_serialization(self): line = Line3d(0, 1, 4, 2, 3, 7) self.assertEqual(str(line), "0 1 4 2 3 7") def test_copy_constructor(self): - lineA = Line3d(0, 1, 4, 2, 3, 7) - lineB = Line3d(lineA) + line_a = Line3d(0, 1, 4, 2, 3, 7) + line_b = Line3d(line_a) - self.assertEqual(lineA, lineB) + self.assertEqual(line_a, line_b) def test_direction(self): - lineA = Line3d(1, 1, 1, 0, 0, 0) - lineB = Line3d(2, 2, 2, 0, 0, 0) - lineC = Line3d(0, 0, 0, 1, 1, 1) - self.assertTrue(lineA.direction() == (lineA[1] - lineA[0]).normalize()) - self.assertTrue(lineA.direction() == lineB.direction()) - self.assertFalse(lineA.direction() == lineC.direction()) + line_a = Line3d(1, 1, 1, 0, 0, 0) + line_b = Line3d(2, 2, 2, 0, 0, 0) + line_c = Line3d(0, 0, 0, 1, 1, 1) + self.assertTrue(line_a.direction() == (line_a[1] - line_a[0]).normalize()) + self.assertTrue(line_a.direction() == line_b.direction()) + self.assertFalse(line_a.direction() == line_c.direction()) - lineA.set(1, 1, 2, 1, 1, 10) - self.assertTrue(lineA.direction() == Vector3d.UNIT_Z) + line_a.set(1, 1, 2, 1, 1, 10) + self.assertTrue(line_a.direction() == Vector3d.UNIT_Z) - lineA.set(1, 5, 1, 1, 1, 1) - self.assertTrue(lineA.direction() == -Vector3d.UNIT_Y) + line_a.set(1, 5, 1, 1, 1, 1) + self.assertTrue(line_a.direction() == -Vector3d.UNIT_Y) - lineA.set(1, 1, 1, 7, 1, 1) - self.assertTrue(lineA.direction() == Vector3d.UNIT_X) + line_a.set(1, 1, 1, 7, 1, 1) + self.assertTrue(line_a.direction() == Vector3d.UNIT_X) def test_within(self): line = Line3d(0, 0, 0, 1, 1, 1) diff --git a/src/python/SignalStats.i b/src/python/SignalStats.i index 9ea9cb7f2..ecc9e8157 100644 --- a/src/python/SignalStats.i +++ b/src/python/SignalStats.i @@ -104,4 +104,4 @@ namespace ignition }; } -} \ No newline at end of file +} diff --git a/src/python/SignalStats_TEST.py b/src/python/SignalStats_TEST.py index 4451b8c00..e8497e6d2 100644 --- a/src/python/SignalStats_TEST.py +++ b/src/python/SignalStats_TEST.py @@ -12,16 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest import math +import unittest from ignition.math import Rand +from ignition.math import SignalMaxAbsoluteValue from ignition.math import SignalMaximum -from ignition.math import SignalMinimum from ignition.math import SignalMean -from ignition.math import SignalVariance +from ignition.math import SignalMinimum from ignition.math import SignalRootMeanSquare -from ignition.math import SignalMaxAbsoluteValue from ignition.math import SignalStats +from ignition.math import SignalVariance class TestSignalStats(unittest.TestCase): @@ -353,13 +353,13 @@ def test_signal_variance_random_values(self): # en.wikipedia.org/wiki/Variance#Distribution_of_the_sample_variance # We will use 5 sigma (4e-5 chance of failure) var = SignalVariance() - stdDev = 3.14159 + std_dev = 3.14159 count = 10000 sigma = 5.0 for i in range(count): - var.insert_data(Rand.dbl_normal(0.0, stdDev)) + var.insert_data(Rand.dbl_normal(0.0, std_dev)) - variance = stdDev*stdDev + variance = std_dev*std_dev sampleVariance2 = 2 * variance*variance / (count - 1) self.assertAlmostEqual(var.value(), variance, delta=sigma*math.sqrt(sampleVariance2)) From 8536f320fffa327f9de46557a269dfe2f34cfdb4 Mon Sep 17 00:00:00 2001 From: Marcos Wagner Date: Wed, 25 Aug 2021 16:14:53 -0300 Subject: [PATCH 5/5] Fixing a typo and trailing spaces Signed-off-by: Marcos Wagner --- src/python/Line2.i | 4 ++-- src/python/Line3_TEST.py | 3 ++- src/python/SignalStats.i | 14 +++++++------- src/python/Temperature.i | 6 +++--- src/python/Temperature_TEST.py | 8 ++++---- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/python/Line2.i b/src/python/Line2.i index 0e8e93c84..658c98ab4 100644 --- a/src/python/Line2.i +++ b/src/python/Line2.i @@ -67,7 +67,7 @@ namespace ignition } } - %extend Line2 + %extend Line2 { std::string __str__() const { std::ostringstream out; @@ -75,7 +75,7 @@ namespace ignition return out.str(); } } - + %template(Line2i) Line2; %template(Line2d) Line2; %template(Line2f) Line2; diff --git a/src/python/Line3_TEST.py b/src/python/Line3_TEST.py index 9d8a61d41..7d5cc11f7 100644 --- a/src/python/Line3_TEST.py +++ b/src/python/Line3_TEST.py @@ -117,7 +117,8 @@ def test_direction(self): line_a = Line3d(1, 1, 1, 0, 0, 0) line_b = Line3d(2, 2, 2, 0, 0, 0) line_c = Line3d(0, 0, 0, 1, 1, 1) - self.assertTrue(line_a.direction() == (line_a[1] - line_a[0]).normalize()) + self.assertTrue(line_a.direction() == + (line_a[1] - line_a[0]).normalize()) self.assertTrue(line_a.direction() == line_b.direction()) self.assertFalse(line_a.direction() == line_c.direction()) diff --git a/src/python/SignalStats.i b/src/python/SignalStats.i index ecc9e8157..b918d3f35 100644 --- a/src/python/SignalStats.i +++ b/src/python/SignalStats.i @@ -30,7 +30,7 @@ namespace ignition namespace math { class SignalMaximum - { + { %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: virtual void Reset(); public: virtual size_t Count() const; @@ -40,7 +40,7 @@ namespace ignition }; class SignalMean - { + { %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: virtual void Reset(); public: virtual size_t Count() const; @@ -50,7 +50,7 @@ namespace ignition }; class SignalMinimum - { + { %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: virtual void Reset(); public: virtual size_t Count() const; @@ -60,7 +60,7 @@ namespace ignition }; class SignalRootMeanSquare - { + { %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: virtual void Reset(); public: virtual size_t Count() const; @@ -70,7 +70,7 @@ namespace ignition }; class SignalMaxAbsoluteValue - { + { %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: virtual void Reset(); public: virtual size_t Count() const; @@ -80,7 +80,7 @@ namespace ignition }; class SignalVariance - { + { %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: virtual void Reset(); public: virtual size_t Count() const; @@ -90,7 +90,7 @@ namespace ignition }; class SignalStats - { + { %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: SignalStats(); public: ~SignalStats(); diff --git a/src/python/Temperature.i b/src/python/Temperature.i index ba6e6426e..27043ea62 100644 --- a/src/python/Temperature.i +++ b/src/python/Temperature.i @@ -27,7 +27,7 @@ namespace ignition namespace math { class Temperature - { + { %rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) ""; public: Temperature(); public: Temperature(const double _temp); @@ -75,8 +75,8 @@ namespace ignition public: bool operator>=(const Temperature &_temp) const; public: bool operator>=(const double _temp) const; }; - - %extend Temperature + + %extend Temperature { std::string __str__() const { std::ostringstream out; diff --git a/src/python/Temperature_TEST.py b/src/python/Temperature_TEST.py index 3fc2e02a1..4955804e5 100644 --- a/src/python/Temperature_TEST.py +++ b/src/python/Temperature_TEST.py @@ -18,7 +18,7 @@ class TestTemperature(unittest.TestCase): - def test_temperatur_constructor(self): + def test_temperature_constructor(self): temp = Temperature() self.assertAlmostEqual(temp.kelvin(), 0.0, 1e-6) @@ -46,7 +46,7 @@ def test_temperatur_constructor(self): self.assertFalse(temp >= 0.1) self.assertTrue(temp >= 0.0) - def test_temperatur_conversions(self): + def test_temperature_conversions(self): self.assertAlmostEqual(Temperature.kelvin_to_celsius(0), -273.15, delta=1e-6) self.assertAlmostEqual(Temperature.kelvin_to_fahrenheit(300), 80.33, @@ -60,7 +60,7 @@ def test_temperatur_conversions(self): self.assertAlmostEqual(Temperature.fahrenheit_to_kelvin(60.0), 288.7055, delta=1e-3) - def test_temperatur_mutators_accessors(self): + def test_temperature_mutators_accessors(self): temp = Temperature() self.assertAlmostEqual(temp.kelvin(), 0.0, delta=1e-6) @@ -74,7 +74,7 @@ def test_temperatur_mutators_accessors(self): self.assertAlmostEqual(temp.fahrenheit(), 30.0, delta=1e-6) self.assertAlmostEqual(temp(), 272.0388889, delta=1e-6) - def test_temperatur_operators(self): + def test_temperature_operators(self): temp = Temperature(20) self.assertAlmostEqual(temp(), 20, delta=1e-6)