From 4be76d2906d7d750d7edc2d5888eacfad9700725 Mon Sep 17 00:00:00 2001
From: Gennady Pospelov <g.pospelov@fz-juelich.de>
Date: Mon, 28 Sep 2015 10:50:47 +0200
Subject: [PATCH] DetectorMask unit tests

---
 Core/Algorithms/inc/DetectorMask.h          |  10 +-
 Core/Algorithms/src/DetectorMask.cpp        |  61 +++++++--
 Core/Geometry/src/Polygon.cpp               |  12 +-
 Core/Tools/inc/BornAgainNamespace.h         |   2 +
 Core/Tools/inc/OutputData.h                 |  24 ++--
 Tests/UnitTests/TestCore/DetectorMaskTest.h | 133 ++++++++++++++++++++
 Tests/UnitTests/TestCore/OutputDataTest.h   |  41 ++++--
 Tests/UnitTests/TestCore/PolygonTest.h      |  20 ++-
 Tests/UnitTests/TestCore/main.cpp           |   1 +
 9 files changed, 261 insertions(+), 43 deletions(-)
 create mode 100644 Tests/UnitTests/TestCore/DetectorMaskTest.h

diff --git a/Core/Algorithms/inc/DetectorMask.h b/Core/Algorithms/inc/DetectorMask.h
index 82afdddd92a..5635df084ae 100644
--- a/Core/Algorithms/inc/DetectorMask.h
+++ b/Core/Algorithms/inc/DetectorMask.h
@@ -35,6 +35,8 @@ class BA_CORE_API_ DetectorMask
 {
 public:
     DetectorMask();
+    DetectorMask(const DetectorMask &other);
+    DetectorMask &operator=(const DetectorMask &other);
 
     //! Add mask to the stack of detector masks. The value "true" means that the area will
     //! be excluded from the analysis.
@@ -49,9 +51,15 @@ public:
 
     const OutputData<bool>* getMaskData() const;
 
+    // clear all masks and return object to initial state
+    void clear();
+
 private:
+    //! swap function
+    void swapContent(DetectorMask &other);
+
     SafePointerVector<Geometry::IShape2D> m_shapes;
-    std::map<const Geometry::IShape2D *, bool> m_shape_to_mask;
+    std::vector<bool> m_mask_of_shape;
     OutputData<bool> m_mask_data;
 };
 
diff --git a/Core/Algorithms/src/DetectorMask.cpp b/Core/Algorithms/src/DetectorMask.cpp
index b1e085b72a0..c3c4c197d15 100644
--- a/Core/Algorithms/src/DetectorMask.cpp
+++ b/Core/Algorithms/src/DetectorMask.cpp
@@ -16,6 +16,7 @@
 #include "DetectorMask.h"
 #include "IShape2D.h"
 #include "Detector.h"
+#include "BornAgainNamespace.h"
 
 
 // InfinitePlane, Line, VerticalLine, HorizontalLine, Ellipse, Rectangle
@@ -24,34 +25,58 @@
 //
 // Polygon stdvector<stdvector<double>> constructor
 
+DetectorMask::DetectorMask()
+{
+
+}
+
+DetectorMask::DetectorMask(const DetectorMask &other)
+    : m_shapes(other.m_shapes)
+    , m_mask_of_shape(other.m_mask_of_shape)
+{
+    m_mask_data.copyFrom(other.m_mask_data);
+}
+
+DetectorMask &DetectorMask::operator=(const DetectorMask &other)
+{
+    if (this != &other) {
+        DetectorMask tmp(other);
+        tmp.swapContent(*this);
+    }
+    return *this;
+
+}
+
 void DetectorMask::addMask(const Geometry::IShape2D &shape, bool mask_value)
 {
-    Geometry::IShape2D *clone = shape.clone();
-    m_shapes.push_back(clone);
-    m_shape_to_mask[clone] = mask_value;
+    m_shapes.push_back(shape.clone());
+    m_mask_of_shape.push_back(mask_value);
+    m_mask_data.clear();
 }
 
 void DetectorMask::initMaskData(const Detector &detector)
 {
     m_mask_data.clear();
-    if(!m_shapes.size()) return;
-
-    assert(m_shapes.size() == m_shape_to_mask.size());
 
     for (size_t dim=0; dim<detector.getDimension(); ++dim) {
         m_mask_data.addAxis(detector.getAxis(dim));
     }
     m_mask_data.setAllTo(false);
 
-    // setting mask to the data starting from last shape added
+    if(!m_shapes.size()) return;
+    assert(m_shapes.size() == m_mask_of_shape.size());
+
     for(size_t index=0; index<m_mask_data.getAllocatedSize(); ++index) {
-        double x = m_mask_data.getAxisValue(index, 0);
-        double y = m_mask_data.getAxisValue(index, 1);
+        Bin1D binx = m_mask_data.getAxisBin(index, BornAgain::X_AXIS_INDEX);
+        Bin1D biny = m_mask_data.getAxisBin(index, BornAgain::Y_AXIS_INDEX);
+        // setting mask to the data starting from last shape added
         for(size_t i_shape=m_shapes.size(); i_shape>0; --i_shape) {
             const Geometry::IShape2D *shape = m_shapes[i_shape-1];
-            if(shape->contains(x, y)) {
-                m_mask_data[index] = m_shape_to_mask[shape];
+            //std::cout << " index:" << index << " i_shape:" << i_shape << " number of shapes:" << m_shapes.size() << std::endl;
+            if(shape->contains(binx, biny)) {
+                m_mask_data[index] = m_mask_of_shape[i_shape-1];
                 // if given index is covered by the shape, stop looking further
+                //std::cout << " break! mask of shape" << m_mask_of_shape[i_shape-1] << std::endl;
                 break;
             }
         }
@@ -70,3 +95,17 @@ const OutputData<bool> *DetectorMask::getMaskData() const
 {
     return &m_mask_data;
 }
+
+void DetectorMask::clear()
+{
+    m_shapes.clear();
+    m_mask_of_shape.clear();
+    m_mask_data.clear();
+}
+
+void DetectorMask::swapContent(DetectorMask &other)
+{
+    std::swap(this->m_shapes, other.m_shapes);
+    std::swap(this->m_mask_of_shape, other.m_mask_of_shape);
+    this->m_mask_data.copyFrom(other.m_mask_data);
+}
diff --git a/Core/Geometry/src/Polygon.cpp b/Core/Geometry/src/Polygon.cpp
index e61653ff5d8..81ecef6dbe0 100644
--- a/Core/Geometry/src/Polygon.cpp
+++ b/Core/Geometry/src/Polygon.cpp
@@ -69,7 +69,7 @@ Polygon *Polygon::clone() const
 bool Polygon::contains(double x, double y) const
 {
 //    return within(PolygonPrivate::point_t(x, y), m_d->polygon); // not including borders
-    return covered_by(PolygonPrivate::point_t(x, y), m_d->polygon);
+    return covered_by(PolygonPrivate::point_t(x, y), m_d->polygon); // including borders
 }
 
 bool Polygon::contains(const Bin1D &binx, const Bin1D &biny) const
@@ -87,16 +87,6 @@ void Polygon::print(std::ostream &ostr) const
     ostr << wkt<PolygonPrivate::polygon_t>(m_d->polygon);
 }
 
-//double Polygon::getData() const
-//{
-//    return m_d->m_x;
-//}
-
-//void Polygon::setData(double value) const
-//{
-//    m_d->m_x = value;
-//}
-
 Polygon::Polygon(const Polygon &other)
 {
     m_d = new PolygonPrivate(*other.m_d);
diff --git a/Core/Tools/inc/BornAgainNamespace.h b/Core/Tools/inc/BornAgainNamespace.h
index c3f0120cda2..db4787a57e5 100644
--- a/Core/Tools/inc/BornAgainNamespace.h
+++ b/Core/Tools/inc/BornAgainNamespace.h
@@ -24,6 +24,8 @@ namespace BornAgain
     static const std::string ALPHA_AXIS_NAME = "alpha_f";
     static const size_t PHI_AXIS_INDEX = 0;
     static const size_t ALPHA_AXIS_INDEX = 1;
+    static const size_t X_AXIS_INDEX = 0;
+    static const size_t Y_AXIS_INDEX = 1;
 
     const std::string FTDistribution2DCauchyType = "FTDistribution2DCauchy";
     const std::string FTDistribution2DGaussType = "FTDistribution2DGauss";
diff --git a/Core/Tools/inc/OutputData.h b/Core/Tools/inc/OutputData.h
index b4d5dd96f64..70d8e2098de 100644
--- a/Core/Tools/inc/OutputData.h
+++ b/Core/Tools/inc/OutputData.h
@@ -174,6 +174,12 @@ public:
     //! @return Vector of corresponding bin centers
     std::vector<double > getAxesValues(size_t global_index) const;
 
+    //! Returns bin of selected axis for given global_index.
+    //! @param global_index The global index of this data structure.
+    //! @param i_selected_axis Serial number of selected axis.
+    //! @return Corresponding Bin1D object
+    Bin1D getAxisBin(size_t global_index, size_t i_selected_axis) const;
+
     //! Returns bin of selected axis for given global_index.
     //! @param global_index The global index of this data structure.
     //! @param axis_name The name of selected axis.
@@ -561,18 +567,17 @@ std::vector<double> OutputData<T>::getAxesValues(size_t global_index) const
     return result;
 }
 
+template <class T>
+Bin1D OutputData<T>::getAxisBin(size_t global_index, size_t i_selected_axis) const
+{
+    int axis_index = getAxisBinIndex(global_index, i_selected_axis);
+    return m_value_axes[i_selected_axis]->getBin(axis_index);
+}
+
 template <class T>
 Bin1D OutputData<T>::getAxisBin(size_t global_index, const std::string& axis_name) const
 {
-    for (size_t i=0; i<m_value_axes.size(); ++i) {
-        if (m_value_axes[i]->getName() == axis_name) {
-            int axis_index = getAxisBinIndex(global_index, i);
-            return m_value_axes[i]->getBin(axis_index);
-        }
-    }
-    throw LogicErrorException(
-                "OutputData<T>::getAxisBin() -> "
-                "Error! Axis with given name not found '" + axis_name + "'");
+    return getAxisBin(global_index, getAxisSerialNumber(axis_name));
 }
 
 template<class T>
@@ -648,6 +653,7 @@ bool OutputData<T>::isInitialized() const
 {
     if(!mp_ll_data) return false;
     if(getRank() != mp_ll_data->getRank()) return false;
+    if(!getRank()) return false;
     return true;
 }
 
diff --git a/Tests/UnitTests/TestCore/DetectorMaskTest.h b/Tests/UnitTests/TestCore/DetectorMaskTest.h
new file mode 100644
index 00000000000..c8a40037ea6
--- /dev/null
+++ b/Tests/UnitTests/TestCore/DetectorMaskTest.h
@@ -0,0 +1,133 @@
+#ifndef DETECTORMASKTEST_H
+#define DETECTORMASKTEST_H
+
+#include "DetectorMask.h"
+#include "Detector.h"
+#include "gtest/gtest.h"
+#include <boost/scoped_ptr.hpp>
+#include <boost/assign/list_of.hpp>
+
+class DetectorMaskTest : public ::testing::Test
+{
+public:
+};
+
+
+TEST_F(DetectorMaskTest, InitialState)
+{
+    DetectorMask test;
+    EXPECT_THROW(test.getMask(0), LogicErrorException);
+    EXPECT_FALSE(test.getMaskData()->isInitialized());
+}
+
+
+//  4.0  -------------------------------------------------------------------------
+//       |  5  |  11 |  17 | 23  | 29  | 35  | 41  | 47  | 53  | 59  | 65  | 71  |
+//  3.0  -------------------------------------------------------------------------
+//       |  4  |  10 |  16 | 22  | 28  | 34  | 40  | 46  | 52  | 58  | 64  | 70  |
+//  2.0  -------------------------------------------------------------------------
+//       |  3  |  9  |  15 | 21  | 27  | 33  | 39  | 45  | 51  | 57  | 63  | 69  |
+//  1.0  -------------------------------------------------------------------------
+//       |  2  |  8  |  14 | 20  | 26  | 32  | 38  | 44  | 50  | 56  | 62  | 68  |
+//  0.0  -------------------------------------------------------------------------
+//       |  1  |  7  |  13 | 19  | 25  | 31  | 37  | 43  | 49  | 55  | 61  | 67  |
+// -1.0  -------------------------------------------------------------------------
+//       |  0  |  6  |  12 | 18  | 24  | 30  | 36  | 42  | 48  | 54  | 60  | 66  |
+// -2.0  -------------------------------------------------------------------------
+//     -4.0  -3.0  -2.0  -1.0   0.0   1.0   2.0   3.0   4.0   5.0   6.0   7.0   8.0
+
+TEST_F(DetectorMaskTest, AddMask)
+{
+    DetectorMask detectorMask;
+
+    std::vector<double> x = boost::assign::list_of(4.0)(-4.0)(-4.0)(4.0)(4.0);
+    std::vector<double> y = boost::assign::list_of(2.0)(2.0)(-2.0)(-2.0)(2.0);
+    Geometry::Polygon polygon(x, y);
+
+    Detector detector;
+    detector.addAxis(FixedBinAxis("x-axis", 12, -4.0, 8.0));
+    detector.addAxis(FixedBinAxis("y-axis", 6, -2.0, 4.0));
+
+    // initializing mask with detector and one shape
+    detectorMask.addMask(polygon, true);
+    detectorMask.initMaskData(detector);
+
+    EXPECT_TRUE(detectorMask.getMaskData()->isInitialized());
+
+    for(size_t index=0; index<detectorMask.getMaskData()->getAllocatedSize(); ++index) {
+        double x = detectorMask.getMaskData()->getAxisValue(index, 0);
+        double y = detectorMask.getMaskData()->getAxisValue(index, 1);
+        if( x>= -4.0 && x <=4.0 && y>=-2.0 && y<=2.0) {
+            EXPECT_TRUE(detectorMask.getMask(index));
+        } else {
+            EXPECT_FALSE(detectorMask.getMask(index));
+        }
+    }
+
+    // adding second mask of same size which discard previous one
+    detectorMask.addMask(polygon, false);
+    detectorMask.initMaskData(detector);
+
+    for(size_t index=0; index<detectorMask.getMaskData()->getAllocatedSize(); ++index) {
+        EXPECT_FALSE(detectorMask.getMask(index));
+    }
+
+    // adding third mask
+    x = boost::assign::list_of(5.0)(5.0)(8.0)(8.0)(5.0);
+    y = boost::assign::list_of(2.0)(4.0)(4.0)(2.0)(2.0);
+    detectorMask.addMask(Geometry::Polygon(x, y), true);
+    detectorMask.initMaskData(detector);
+    for(size_t index=0; index<detectorMask.getMaskData()->getAllocatedSize(); ++index) {
+        double x = detectorMask.getMaskData()->getAxisValue(index, 0);
+        double y = detectorMask.getMaskData()->getAxisValue(index, 1);
+        if( x>= 5.0 && x <=8.0 && y>=2.0 && y<=4.0) {
+            EXPECT_TRUE(detectorMask.getMask(index));
+        } else {
+            EXPECT_FALSE(detectorMask.getMask(index));
+        }
+    }
+
+    // clearing all masks
+    detectorMask.clear();
+    detectorMask.initMaskData(detector);
+    for(size_t index=0; index<detectorMask.getMaskData()->getAllocatedSize(); ++index) {
+        EXPECT_FALSE(detectorMask.getMask(index));
+    }
+
+}
+
+
+TEST_F(DetectorMaskTest, AssignmentOperator)
+{
+    DetectorMask detectorMask;
+
+    std::vector<double> x = boost::assign::list_of(4.0)(-4.0)(-4.0)(4.0)(4.0);
+    std::vector<double> y = boost::assign::list_of(2.0)(2.0)(-2.0)(-2.0)(2.0);
+    Geometry::Polygon polygon(x, y);
+
+    Detector detector;
+    detector.addAxis(FixedBinAxis("x-axis", 12, -4.0, 8.0));
+    detector.addAxis(FixedBinAxis("y-axis", 6, -2.0, 4.0));
+
+    // initializing mask with detector and one shape
+    detectorMask.addMask(polygon, true);
+    detectorMask.initMaskData(detector);
+
+    DetectorMask mask = detectorMask;
+
+    EXPECT_TRUE(mask.getMaskData()->isInitialized());
+
+    for(size_t index=0; index<mask.getMaskData()->getAllocatedSize(); ++index) {
+        double x = mask.getMaskData()->getAxisValue(index, 0);
+        double y = mask.getMaskData()->getAxisValue(index, 1);
+        if( x>= -4.0 && x <=4.0 && y>=-2.0 && y<=2.0) {
+            EXPECT_TRUE(mask.getMask(index));
+        } else {
+            EXPECT_FALSE(mask.getMask(index));
+        }
+    }
+
+}
+
+
+#endif
diff --git a/Tests/UnitTests/TestCore/OutputDataTest.h b/Tests/UnitTests/TestCore/OutputDataTest.h
index b3b81c67e9f..616d3c50551 100644
--- a/Tests/UnitTests/TestCore/OutputDataTest.h
+++ b/Tests/UnitTests/TestCore/OutputDataTest.h
@@ -89,15 +89,15 @@ TEST_F(OutputDataTest, DataInitialization)
     EXPECT_EQ(Eigen::Matrix2d::Identity(), matrix_data_2d[matrix_data_2d.toGlobalIndex(coordinates2)]);
 }
 
-//TEST_F(OutputDataTest, isInitialized)
-//{
-//    OutputData<double> data1;
-//    EXPECT_FALSE(data1.isInitialized());
-//    data1.addAxis("axis", 10, 0.0, 10.0);
-//    EXPECT_TRUE(data1.isInitialized());
-//    data1.clear();
-//    EXPECT_FALSE(data1.isInitialized());
-//}
+TEST_F(OutputDataTest, isInitialized)
+{
+    OutputData<double> data1;
+    EXPECT_FALSE(data1.isInitialized());
+    data1.addAxis("axis", 10, 0.0, 10.0);
+    EXPECT_TRUE(data1.isInitialized());
+    data1.clear();
+    EXPECT_FALSE(data1.isInitialized());
+}
 
 TEST_F(OutputDataTest, DataCopying)
 {
@@ -224,6 +224,29 @@ TEST_F(OutputDataTest, ValueOfAxis)
     EXPECT_EQ( 7.5, mdata.getAxisValue(19, "axis2"));
 }
 
+// y |
+// --------------------------------------------
+// 1 | 1   3   5   7   9   11  13  15  17  19 |
+// 0 | 0   2   4   6   8   10  12  14  16  18 |
+// --------------------------------------------
+//   | 0   1   2   3   4   5   6   7   8   9  | x
+TEST_F(OutputDataTest, GetAxisBin)
+{
+    OutputData<double > data;
+    data.addAxis("axis1", 10, 0., 10.);
+    data.addAxis("axis2", 2, 0., 10.);
+
+    EXPECT_EQ(0.5, data.getAxisBin(0, "axis1").getMidPoint());
+    EXPECT_EQ(0.5, data.getAxisBin(1, "axis1").getMidPoint());
+    EXPECT_EQ(9.5, data.getAxisBin(18, "axis1").getMidPoint());
+    EXPECT_EQ(9.5, data.getAxisBin(19, "axis1").getMidPoint());
+    EXPECT_EQ(2.5, data.getAxisBin(0, "axis2").getMidPoint());
+    EXPECT_EQ(7.5, data.getAxisBin(1, "axis2").getMidPoint());
+    EXPECT_EQ(2.5, data.getAxisBin(18, "axis2").getMidPoint());
+    EXPECT_EQ(7.5, data.getAxisBin(19, "axis2").getMidPoint());
+}
+
+
 
 // y |
 // --------------------------------------------
diff --git a/Tests/UnitTests/TestCore/PolygonTest.h b/Tests/UnitTests/TestCore/PolygonTest.h
index 8607d84bbfe..72bf43fb403 100644
--- a/Tests/UnitTests/TestCore/PolygonTest.h
+++ b/Tests/UnitTests/TestCore/PolygonTest.h
@@ -37,7 +37,6 @@ TEST_F(PolygonTest, SimpleRectangle)
     EXPECT_TRUE(polygon2.contains(-4.0, -2.0));
     EXPECT_FALSE(polygon2.contains(0.0, 2.01));
     EXPECT_FALSE(polygon2.contains(4.0, -2.01));
-
 }
 
 //     *******
@@ -53,7 +52,7 @@ TEST_F(PolygonTest, SandWatchShape)
     std::vector<double> x = boost::assign::list_of(2.0)(-2.0)(2.0)(-2.0)(2.0);
     std::vector<double> y = boost::assign::list_of(2.0)(2.0)(-2.0)(-2.0)(2.0);
     Geometry::Polygon polygon(x, y);
-    std::cout << polygon << std::endl;
+//    std::cout << polygon << std::endl;
 
     // for some reason area calculation doesn't work for boost's polygon of such shape
     // EXPECT_DOUBLE_EQ(8.0, polygon.getArea());
@@ -68,6 +67,23 @@ TEST_F(PolygonTest, SandWatchShape)
 
 }
 
+TEST_F(PolygonTest, ContainsBin)
+{
+    // simple closed rectangle
+    std::vector<double> x = boost::assign::list_of(4.0)(-4.0)(-4.0)(4.0)(4.0);
+    std::vector<double> y = boost::assign::list_of(2.0)(2.0)(-2.0)(-2.0)(2.0);
+    Geometry::Polygon polygon(x, y);
+
+    Bin1D binx1(3.5, 4.5);
+    Bin1D biny1(1.5, 2.5);
+    EXPECT_TRUE(polygon.contains(binx1, biny1));
+
+    Bin1D binx2(3.5, 4.6);
+    Bin1D biny2(1.5, 2.6);
+    EXPECT_FALSE(polygon.contains(binx2, biny2));
+}
+
+
 TEST_F(PolygonTest, Clone)
 {
     std::vector<double> x = boost::assign::list_of(4.0)(-4.0)(-4.0)(4.0)(4.0);
diff --git a/Tests/UnitTests/TestCore/main.cpp b/Tests/UnitTests/TestCore/main.cpp
index bf4513f0e06..98a456f7af9 100644
--- a/Tests/UnitTests/TestCore/main.cpp
+++ b/Tests/UnitTests/TestCore/main.cpp
@@ -54,6 +54,7 @@
 #include "Histogram1DTest.h"
 #include "Histogram2DTest.h"
 #include "PolygonTest.h"
+#include "DetectorMaskTest.h"
 
 
 
-- 
GitLab