diff --git a/Core/Export/SimulationToPython.cpp b/Core/Export/SimulationToPython.cpp
index 2f6fd8416e034f11ef67c0e37e79602317384dcb..e82a9f34b305d8b4724ac6feba6a0b473a18b880 100644
--- a/Core/Export/SimulationToPython.cpp
+++ b/Core/Export/SimulationToPython.cpp
@@ -28,7 +28,6 @@
 #include "Device/Beam/FootprintSquare.h"
 #include "Device/Detector/RectangularDetector.h"
 #include "Device/Detector/SphericalDetector.h"
-#include "Device/ProDetector/RegionOfInterest.h"
 #include "Device/Resolution/ConvolutionDetectorResolution.h"
 #include "Device/Resolution/ResolutionFunction2DGaussian.h"
 #include "Device/Resolution/ScanResolution.h"
@@ -40,7 +39,7 @@ using pyfmt::indent;
 
 namespace {
 
-//! Returns a function that converts a coordinate to a Python code snippet with appropiate unit
+//! Returns a function that converts a coordinate to a Python code snippet with appropriate unit
 std::function<std::string(double)> printFunc(const IDetector* detector)
 {
     if (detector->defaultCoords() == Axes::Coords::MM)
@@ -198,13 +197,14 @@ std::string defineDetector(const ISimulation* simulation)
             throw std::runtime_error("defineDetector() -> Error. Unknown alignment.");
     } else
         throw std::runtime_error("defineDetector() -> Error. Unknown detector");
-    if (detector->regionOfInterest()) { // #baROI refactor: detector->hasRoi()
-        // #baROI  refactor : detector->roiCoords()
+    if (detector->hasExplicitRegionOfInterest()) {
+        const auto xBounds = detector->regionOfInterestBounds(0);
+        const auto yBounds = detector->regionOfInterestBounds(1);
         result << indent() << "detector.setRegionOfInterest("
-               << printFunc(detector)(detector->regionOfInterest()->getXlow()) << ", "
-               << printFunc(detector)(detector->regionOfInterest()->getYlow()) << ", "
-               << printFunc(detector)(detector->regionOfInterest()->getXup()) << ", "
-               << printFunc(detector)(detector->regionOfInterest()->getYup()) << ")\n";
+               << printFunc(detector)(xBounds.first) << ", "
+               << printFunc(detector)(yBounds.first) << ", "
+               << printFunc(detector)(xBounds.second) << ", "
+               << printFunc(detector)(yBounds.second) << ")\n";
     }
     return result.str();
 }
@@ -356,7 +356,7 @@ std::string defineMasks(const ISimulation* simulation)
 
     const IDetector* detector = simulation->instrument().getDetector();
     const DetectorMask* detectorMask = detector->detectorMask();
-    if (detectorMask && detectorMask->numberOfMasks()) {
+    if (detectorMask && detectorMask->hasMasks()) {
         result << "\n";
         for (size_t i_mask = 0; i_mask < detectorMask->numberOfMasks(); ++i_mask) {
             bool mask_value(false);
diff --git a/Core/Simulation/OffSpecularSimulation.cpp b/Core/Simulation/OffSpecularSimulation.cpp
index 34e1ae786cb3bdf0d7fd2976f8ebd385771b9a4f..624b421f550ab1796cdac135bc363b5a2a1f4c3c 100644
--- a/Core/Simulation/OffSpecularSimulation.cpp
+++ b/Core/Simulation/OffSpecularSimulation.cpp
@@ -13,6 +13,9 @@
 //  ************************************************************************************************
 
 #include "Core/Simulation/OffSpecularSimulation.h"
+#include "Base/Pixel/RectangularPixel.h"
+#include "Device/Detector/RectangularDetector.h"
+#include "Device/Detector/SphericalDetector.h"
 #include "Device/Instrument/CoordSystem2D.h"
 #include "Param/Distrib/Distributions.h"
 #include "Sample/SampleBuilderEngine/ISampleBuilder.h"
@@ -43,8 +46,8 @@ size_t OffSpecularSimulation::numberOfSimulationElements() const
 SimulationResult OffSpecularSimulation::result() const
 {
     auto data = std::unique_ptr<OutputData<double>>(m_intensity_map.clone());
-    OffSpecularCoordinates converter(detector2D(), beam(), *m_alpha_i_axis);
-    return SimulationResult(*data, converter);
+    std::unique_ptr<ICoordSystem> coordsSystem(createCoordSystem());
+    return SimulationResult(*data, *coordsSystem);
 }
 
 void OffSpecularSimulation::setBeamParameters(double wavelength, const IAxis& alpha_axis,
@@ -67,11 +70,23 @@ const IAxis* OffSpecularSimulation::beamAxis() const
 #ifndef SWIG
 ICoordSystem* OffSpecularSimulation::createCoordSystem() const
 {
-    const IAxis* axis = beamAxis();
-    if (!axis)
+    if (!beamAxis())
         throw std::runtime_error("Error in OffSpecularSimulation::createCoordSystem:"
                                  " missing inclination angle axis");
-    return new OffSpecularCoordinates(detector2D(), beam(), *axis);
+
+    const auto axes = detector2D().axesClippedToRegionOfInterest();
+    ASSERT(axes.size() == 2);
+    const IAxis& yAxis = *axes[1];
+
+    if (const auto* rect_det = dynamic_cast<const RectangularDetector*>(&detector2D())) {
+        std::unique_ptr<RectangularPixel> det_pixel(rect_det->regionOfInterestPixel());
+        return OffSpecularCoordinates::createForRectangularDetector(beam(), *beamAxis(), *det_pixel,
+                                                                    yAxis);
+    } else if (dynamic_cast<const SphericalDetector*>(&detector2D()))
+        return OffSpecularCoordinates::createForSphericalDetector(beam(), *beamAxis(), yAxis);
+
+    ASSERT(0);
+    return nullptr;
 }
 #endif
 
diff --git a/Device/Detector/IDetector.cpp b/Device/Detector/IDetector.cpp
index 6bceef47dd6dfdff0ab0b924f6f189141848af0c..aa7e62759de2663ec764c626c77377955e053154 100644
--- a/Device/Detector/IDetector.cpp
+++ b/Device/Detector/IDetector.cpp
@@ -15,7 +15,6 @@
 #include "Device/Detector/IDetector.h"
 #include "Base/Pixel/SimulationElement.h"
 #include "Device/ProDetector/DetectorMask.h"
-#include "Device/ProDetector/RegionOfInterest.h"
 #include "Device/Resolution/ConvolutionDetectorResolution.h"
 
 IDetector::IDetector()
@@ -28,6 +27,7 @@ IDetector::IDetector(const IDetector& other)
     , INode()
     , m_axes(other.m_axes)
     , m_detection_properties(other.m_detection_properties)
+    , m_explicitROI(other.m_explicitROI)
 {
     if (other.m_detector_resolution)
         setDetectorResolution(*other.m_detector_resolution);
@@ -84,6 +84,31 @@ std::unique_ptr<IAxis> IDetector::createAxis(size_t index, size_t n_bins, double
     return std::make_unique<FixedBinAxis>(axisName(index), n_bins, min, max);
 }
 
+size_t IDetector::sizeOfExplicitRegionOfInterest() const
+{
+    if (m_explicitROI.size() != m_axes.size())
+        return 0;
+
+    size_t s = 1;
+    for (const auto& roiOfAxis : m_explicitROI)
+        s *= roiOfAxis.roiSize;
+
+    return s;
+}
+
+std::pair<double, double> IDetector::boundsOfExplicitRegionOfInterest(size_t iAxis) const
+{
+    if (iAxis >= dimension())
+        throw std::runtime_error(
+            "IDetector::boundsOfExplicitRegionOfInterest() -> Error. iAxis must be smaller than "
+            + std::to_string(dimension()));
+
+    if (iAxis < m_explicitROI.size())
+        return {m_explicitROI[iAxis].lower, m_explicitROI[iAxis].upper};
+
+    return {0., 0.};
+}
+
 size_t IDetector::totalSize() const
 {
     const size_t dim = dimension();
@@ -97,18 +122,42 @@ size_t IDetector::totalSize() const
 
 size_t IDetector::sizeOfRegionOfInterest() const
 {
-    if (regionOfInterest() != nullptr)
-        return regionOfInterest()->roiSize();
+    const auto explicitSize = sizeOfExplicitRegionOfInterest();
+    return (explicitSize != 0) ? explicitSize : totalSize();
+}
+
+bool IDetector::hasExplicitRegionOfInterest() const
+{
+    return !m_axes.empty() && (m_explicitROI.size() == m_axes.size());
+}
 
-    return totalSize();
+CloneableVector<IAxis> IDetector::axesClippedToRegionOfInterest() const
+{
+    CloneableVector<IAxis> result;
+    for (size_t iAxis = 0; iAxis < m_axes.size(); ++iAxis) {
+        auto axis = m_axes[iAxis]->clone();
+        axis->clip(regionOfInterestBounds(iAxis));
+        result.push_back(axis);
+    }
+    return result;
 }
 
 size_t IDetector::regionOfInterestIndexToDetectorIndex(const size_t regionOfInterestIndex) const
 {
-    if (regionOfInterest() != nullptr)
-        return regionOfInterest()->detectorIndex(regionOfInterestIndex);
+    if (!hasExplicitRegionOfInterest())
+        return regionOfInterestIndex;
 
-    return regionOfInterestIndex;
+    throw std::runtime_error("IDetector::regionOfInterestIndexToDetectorIndex() -> Error. Not "
+                             "implemented by derived class.");
+}
+
+size_t IDetector::detectorIndexToRegionOfInterestIndex(const size_t detectorIndex) const
+{
+    if (!hasExplicitRegionOfInterest())
+        return detectorIndex;
+
+    throw std::runtime_error("IDetector::detectorIndexToRegionOfInterestIndex() -> Error. Not "
+                             "implemented by derived class.");
 }
 
 void IDetector::setAnalyzerProperties(const kvector_t direction, double efficiency,
@@ -130,6 +179,11 @@ void IDetector::setResolutionFunction(const IResolutionFunction2D& resFunc)
     setDetectorResolution(convFunc);
 }
 
+void IDetector::resetRegionOfInterest()
+{
+    m_explicitROI.clear();
+}
+
 void IDetector::applyDetectorResolution(OutputData<double>* p_intensity_map) const
 {
     if (!p_intensity_map)
@@ -178,11 +232,11 @@ std::unique_ptr<OutputData<double>> IDetector::createDetectorMap() const
             "Error in IDetector::createDetectorMap: dimensions of the detector are undefined");
 
     std::unique_ptr<OutputData<double>> result(new OutputData<double>);
-    for (size_t i = 0; i < dim; ++i)
-        if (auto roi = regionOfInterest())
-            result->addAxis(*roi->clipAxisToRoi(i, axis(i)));
-        else
-            result->addAxis(axis(i));
+    for (size_t iAxis = 0; iAxis < dim; ++iAxis) {
+        std::unique_ptr<IAxis> tempAxis(axis(iAxis).clone());
+        tempAxis->clip(regionOfInterestBounds(iAxis));
+        result->addAxis(*tempAxis);
+    }
 
     return result;
 }
@@ -205,6 +259,20 @@ size_t IDetector::numberOfSimulationElements() const
     return result;
 }
 
+std::pair<double, double> IDetector::regionOfInterestBounds(size_t iAxis) const
+{
+    if (iAxis >= m_axes.size())
+        throw std::runtime_error(
+            "IDetector::regionOfInterestBounds() -> Error. iAxis must be smaller than "
+            + std::to_string(m_axes.size()));
+
+    const auto explicitBounds = boundsOfExplicitRegionOfInterest(iAxis);
+    if (explicitBounds.first != 0 || explicitBounds.second != 0)
+        return explicitBounds;
+
+    return m_axes[iAxis]->bounds();
+}
+
 std::vector<const INode*> IDetector::getChildren() const
 {
     return std::vector<const INode*>() << &m_detection_properties << m_detector_resolution;
@@ -247,3 +315,17 @@ SimulationAreaIterator IDetector::endRegionOfInterestPoints() const
 {
     return SimulationAreaIterator::createEnd(this, SimulationAreaIterator::regionOfInterest);
 }
+
+/* -- IDetector::RoiOfAxis ----------------------------------------------------------- */
+
+IDetector::RoiOfAxis::RoiOfAxis(const IAxis& axis, double _lower, double _upper)
+{
+    lower = _lower;
+    upper = _upper;
+
+    lowerIndex = axis.findClosestIndex(lower);
+    upperIndex = axis.findClosestIndex(upper);
+
+    detectorSize = axis.size();
+    roiSize = upperIndex - lowerIndex + 1;
+}
diff --git a/Device/Detector/IDetector.h b/Device/Detector/IDetector.h
index 8218f47dc4f6d3f84e4a1f4b062eec6908a3d157..4fdef6d51cd87ba144e44c451af924e3ecfc6b23 100644
--- a/Device/Detector/IDetector.h
+++ b/Device/Detector/IDetector.h
@@ -29,9 +29,22 @@ class IDetectorResolution;
 class IResolutionFunction2D;
 template <class T> class OutputData;
 class SimulationElement;
-class RegionOfInterest;
 
 //! Abstract detector interface.
+//!
+//! Handles also "region of interest" (ROI). In general, the ROI is the whole
+//! detector, and all methods related to ROI work on the whole detector.
+//! If a ROI different to the detector size is explicitly set, then ROI-related
+//! methods work on this reduced ROI.
+//! Therefore, when calling ROI related methods, the distinction between "explicit
+//! ROI exists: yes/no" does not have to be made by the caller, but it is handled in here.
+//! For access to the whole detector, even if a smaller ROI is explicitly defined, use the
+//! non-ROI-related methods like totalSize() or axes().
+//! Any method which is not speaking of "explicit ROI" handles the "implicit ROI", i.e. uses
+//! an explicitly set ROI or the whole detector if no explicit ROI exists.
+//! To access the explicitly set ROI, use the methods which have the explicit in its
+//! name, like sizeOfExplicitRegionOfInterest().
+//!
 //! @ingroup detector
 
 class IDetector : public ICloneable, public INode {
@@ -56,18 +69,21 @@ public:
     void setResolutionFunction(const IResolutionFunction2D& resFunc);
 
     //! Resets region of interest making whole detector plane available for the simulation.
-    virtual void resetRegionOfInterest() = 0;
+    void resetRegionOfInterest();
+
     //! Returns detector masks container
     virtual const DetectorMask* detectorMask() const = 0;
 
     std::vector<const INode*> getChildren() const override;
 
     //! Iterate over all points within "region of interest", no matter whether they are masked or
-    //! not.
+    //! not. If no region of interest is explicitly defined, then the whole detector is taken as
+    //! "region of interest".
     void iterateOverRegionOfInterest(std::function<void(const_iterator)> func) const;
 
-    //! Iterate over all non-masked points. Points outside of "region of
-    //! interest" are also treated as "masked".
+    //! Iterate over all non-masked points within "region of interest".
+    //! If no region of interest is explicitly defined, then the whole detector is taken as
+    //! "region of interest".
     void iterateOverNonMaskedPoints(std::function<void(const_iterator)> func) const;
 
 #ifndef SWIG
@@ -80,17 +96,24 @@ public:
     SimulationAreaIterator endNonMaskedPoints() const;
 
     //! Create begin-iterator to iterate over all points which lay
-    //! within the "Region of Interest".
+    //! within the "Region of Interest". If no region of interest is explicitly
+    //! defined, then the whole detector is taken as "region of interest".
     //! No matter whether masked or not.
     SimulationAreaIterator beginRegionOfInterestPoints() const;
 
     //! Create end-iterator to iterate over all points which lay
-    //! within the "Region of Interest".
+    //! within the "Region of Interest". If no region of interest is explicitly
+    //! defined, then the whole detector is taken as "region of interest".
     //! No matter whether masked or not.
     SimulationAreaIterator endRegionOfInterestPoints() const;
 #endif // SWIG
 
+    //! The axes of the complete detector.
+    //! Any region of interest is not taken into account.
     const CloneableVector<IAxis>& axes() const { return m_axes; }
+
+    //! One axis of the complete detector.
+    //! Any region of interest is not taken into account.
     const IAxis& axis(size_t index) const;
 
     //! Returns number of defined axes
@@ -99,17 +122,26 @@ public:
     //! Calculate axis index for given global index
     size_t axisBinIndex(size_t index, size_t selected_axis) const;
 
-    //! Returns total number of pixels // #baROI rename to totalPixels
+    //! Returns total number of pixels.
+    //! Any region of interest is not taken into account.
     size_t totalSize() const;
 
     //! The size of the "Region of Interest". Same as totalSize()
-    //! if no region of interest has been set.
+    //! if no region of interest has been explicitly set.
     size_t sizeOfRegionOfInterest() const;
 
+    //! True if a region of interest is explicitly set.
+    bool hasExplicitRegionOfInterest() const;
+
+    //! Returns the axes clipped to the region of interest. If no region of interest is explicitly
+    //! defined, then the whole detector is taken as "region of interest".
+    CloneableVector<IAxis> axesClippedToRegionOfInterest() const;
+
     //! Convert an index of the region of interest to an index of the detector.
     //! If no region of interest is set, then the index stays unmodified (since ROI == detector
     //! area).
-    size_t regionOfInterestIndexToDetectorIndex(const size_t regionOfInterestIndex) const;
+    virtual size_t regionOfInterestIndexToDetectorIndex(const size_t regionOfInterestIndex) const;
+    virtual size_t detectorIndexToRegionOfInterestIndex(const size_t detectorIndex) const;
 
     //! Applies the detector resolution to the given intensity maps
     void applyDetectorResolution(OutputData<double>* p_intensity_map) const;
@@ -136,8 +168,10 @@ public:
     //! Returns number of simulation elements.
     size_t numberOfSimulationElements() const;
 
-    //! Returns region of  interest if exists.
-    virtual const RegionOfInterest* regionOfInterest() const = 0;
+    //! The lower and upper bound of the region of interest. If no region of interest is explicitly
+    //! defined, then the whole detector is taken as "region of interest".
+    // #baROI add unit test for no ROI (whether internal comparison to 0/0 works...)
+    std::pair<double, double> regionOfInterestBounds(size_t iAxis) const;
 
 protected:
     IDetector(const IDetector& other);
@@ -151,6 +185,37 @@ protected:
     virtual std::unique_ptr<IAxis> createAxis(size_t index, size_t n_bins, double min,
                                               double max) const;
 
+    //! Return 0 if no ROI has been explicitly set.
+    //! Size means number of data points.
+    virtual size_t sizeOfExplicitRegionOfInterest() const;
+
+    //! Lower and upper bound of one axis of an explicitly set ROI.
+    //! Return 0/0 if no ROI has been explicitly set.
+    virtual std::pair<double, double> boundsOfExplicitRegionOfInterest(size_t iAxis) const;
+
+#ifndef SWIG
+protected:
+    //! Keeps RegionOfInterest (ROI) data of one axis
+    struct RoiOfAxis {
+        double lower;
+        double upper;
+
+        // The following values are all pre-computed values. Aim is to speed up repeated
+        // calculations.
+        size_t lowerIndex;   //!< index corresponding to 'lower'
+        size_t upperIndex;   //!< index corresponding to 'upper'
+        size_t roiSize;      //!< number of bins on axis of ROI
+        size_t detectorSize; //!< number of bins on axis of detector
+
+        RoiOfAxis(const IAxis& axis, double lower, double upper);
+    };
+
+    std::vector<RoiOfAxis> m_explicitROI; //!< an explicitly defined region of interest.
+                                          //!< Empty if no ROI has been defined.
+                                          //!< Vector index corresponds to axis index in m_axes
+
+#endif // SWIG
+
 private:
     void setDataToDetectorMap(OutputData<double>& detectorMap,
                               const std::vector<SimulationElement>& elements) const;
diff --git a/Device/Detector/IDetector2D.cpp b/Device/Detector/IDetector2D.cpp
index b4118604a1812380e2b863e84650929aa5b99ecd..41cee4b5e1e95f227a74a806917b7ec4a4923af5 100644
--- a/Device/Detector/IDetector2D.cpp
+++ b/Device/Detector/IDetector2D.cpp
@@ -18,15 +18,26 @@
 #include "Device/Beam/Beam.h"
 #include "Device/Detector/DetectorContext.h"
 #include "Device/Mask/InfinitePlane.h"
-#include "Device/ProDetector/RegionOfInterest.h"
+
+namespace {
+
+inline size_t xcoord(size_t index, size_t sizeX, size_t sizeY)
+{
+    return index / sizeY % sizeX;
+}
+
+inline size_t ycoord(size_t index, size_t sizeY)
+{
+    return index % sizeY;
+}
+
+} // namespace
 
 IDetector2D::IDetector2D() = default;
 
 IDetector2D::IDetector2D(const IDetector2D& other)
     : IDetector(other), m_detector_mask(other.m_detector_mask)
 {
-    if (other.regionOfInterest())
-        m_region_of_interest.reset(other.regionOfInterest()->clone());
 }
 
 IDetector2D::~IDetector2D() = default;
@@ -39,21 +50,45 @@ void IDetector2D::setDetectorParameters(size_t n_x, double x_min, double x_max,
     addAxis(*createAxis(1, n_y, y_min, y_max));
 }
 
-const RegionOfInterest* IDetector2D::regionOfInterest() const
+void IDetector2D::setRegionOfInterest(double xlow, double ylow, double xup, double yup)
 {
-    return m_region_of_interest.get();
+    ASSERT(dimension() == 2);
+
+    m_explicitROI.clear();
+    m_explicitROI.push_back({axis(0), xlow, xup});
+    m_explicitROI.push_back({axis(1), ylow, yup});
 }
 
-void IDetector2D::setRegionOfInterest(double xlow, double ylow, double xup, double yup)
+size_t IDetector2D::regionOfInterestIndexToDetectorIndex(const size_t regionOfInterestIndex) const
 {
-    m_region_of_interest = std::make_unique<RegionOfInterest>(axes(), xlow, ylow, xup, yup);
-    m_detector_mask.initMaskData(axes());
+    if (m_explicitROI.size() != 2)
+        return regionOfInterestIndex;
+
+    const auto& x = m_explicitROI[0];
+    const auto& y = m_explicitROI[1];
+
+    const size_t globalIndex0 = y.lowerIndex + x.lowerIndex * y.detectorSize;
+    return globalIndex0 + ycoord(regionOfInterestIndex, y.roiSize)
+           + xcoord(regionOfInterestIndex, x.roiSize, y.roiSize) * y.detectorSize;
 }
 
-void IDetector2D::resetRegionOfInterest()
+size_t IDetector2D::detectorIndexToRegionOfInterestIndex(const size_t detectorIndex) const
 {
-    m_region_of_interest.reset();
-    m_detector_mask.initMaskData(axes());
+    if (m_explicitROI.size() != 2)
+        return detectorIndex;
+
+    const auto& x = m_explicitROI[0];
+    const auto& y = m_explicitROI[1];
+
+    const size_t ny = ycoord(detectorIndex, y.detectorSize);
+    if (ny < y.lowerIndex || ny > y.upperIndex)
+        throw std::runtime_error("IDetector2D::detectorIndexToRegionOfInterestIndex() -> Error.");
+
+    const size_t nx = xcoord(detectorIndex, x.detectorSize, y.detectorSize);
+    if (nx < x.lowerIndex || nx > x.upperIndex)
+        throw std::runtime_error("IDetector2D::detectorIndexToRegionOfInterestIndex() -> Error.");
+
+    return ny - y.lowerIndex + (nx - x.lowerIndex) * y.roiSize;
 }
 
 std::vector<size_t> IDetector2D::active_indices() const
diff --git a/Device/Detector/IDetector2D.h b/Device/Detector/IDetector2D.h
index 2cd34becc32e6bfa0ec5a310ab502b96397eb986..27563770af35598064973b1964ffd26734aa2761 100644
--- a/Device/Detector/IDetector2D.h
+++ b/Device/Detector/IDetector2D.h
@@ -52,14 +52,12 @@ public:
     //! Put the mask for all detector channels (i.e. exclude whole detector from the analysis)
     void maskAll();
 
-    //! Returns region of  interest if exists.
-    const RegionOfInterest* regionOfInterest() const override;
-
     //! Sets rectangular region of interest with lower left and upper right corners defined.
     void setRegionOfInterest(double xlow, double ylow, double xup, double yup);
 
-    //! Resets region of interest making whole detector plane available for the simulation.
-    void resetRegionOfInterest() override;
+    virtual size_t
+    regionOfInterestIndexToDetectorIndex(const size_t regionOfInterestIndex) const override;
+    virtual size_t detectorIndexToRegionOfInterestIndex(const size_t detectorIndex) const override;
 
     //! Returns vector of unmasked detector indices.
     std::vector<size_t> active_indices() const;
@@ -84,7 +82,6 @@ protected:
 
 private:
     DetectorMask m_detector_mask;
-    std::unique_ptr<RegionOfInterest> m_region_of_interest;
 };
 
 #endif // BORNAGAIN_DEVICE_DETECTOR_IDETECTOR2D_H
diff --git a/Device/Detector/RectangularDetector.cpp b/Device/Detector/RectangularDetector.cpp
index db602c91555cb30384142cea7dd59922d09510f8..8fb23d1674cdb5a6efaed07561aa235e4b3c29a4 100644
--- a/Device/Detector/RectangularDetector.cpp
+++ b/Device/Detector/RectangularDetector.cpp
@@ -18,7 +18,6 @@
 #include "Base/Pixel/RectangularPixel.h"
 #include "Base/Pixel/SimulationElement.h"
 #include "Device/Beam/Beam.h"
-#include "Device/ProDetector/RegionOfInterest.h"
 #include "Device/Resolution/IDetectorResolution.h"
 
 RectangularDetector::RectangularDetector(size_t nxbins, double width, size_t nybins, double height)
@@ -163,22 +162,14 @@ RectangularDetector::EDetectorArrangement RectangularDetector::getDetectorArrang
 
 RectangularPixel* RectangularDetector::regionOfInterestPixel() const
 {
-    const IAxis& u_axis = axis(0);
-    const IAxis& v_axis = axis(1);
-    double u_min, v_min, width, height;
-    auto p_roi =
-        regionOfInterest(); // #baROI refactor: return x1/y1/x2/y2 instead of class RegionOfInterest
-    if (p_roi) {
-        u_min = p_roi->getXlow();
-        v_min = p_roi->getYlow();
-        width = p_roi->getXup() - p_roi->getXlow();
-        height = p_roi->getYup() - p_roi->getYlow();
-    } else {
-        u_min = u_axis.lowerBound();
-        v_min = v_axis.lowerBound();
-        width = getWidth();
-        height = getHeight();
-    }
+    const auto uAxisBoundsROI = regionOfInterestBounds(0);
+    const auto vAxisBoundsROI = regionOfInterestBounds(1);
+
+    const double u_min = uAxisBoundsROI.first;
+    const double v_min = vAxisBoundsROI.first;
+    const double width = uAxisBoundsROI.second - uAxisBoundsROI.first;
+    const double height = vAxisBoundsROI.second - vAxisBoundsROI.first;
+
     const kvector_t corner_position(m_normal_to_detector + (u_min - m_u0) * m_u_unit
                                     + (v_min - m_v0) * m_v_unit);
     const kvector_t uaxis_vector = width * m_u_unit;
diff --git a/Device/Detector/SpecularDetector1D.h b/Device/Detector/SpecularDetector1D.h
index cdaecb3febab05492ca543df2c3b4a007cb6ed7d..9b8358168f49ccad0fc0fcec852f1e0b3b1fc6d2 100644
--- a/Device/Detector/SpecularDetector1D.h
+++ b/Device/Detector/SpecularDetector1D.h
@@ -40,11 +40,6 @@ public:
     //! Returns detector masks container.
     const DetectorMask* detectorMask() const override { return nullptr; }
 
-    //! Returns region of interest if exists.
-    const RegionOfInterest* regionOfInterest() const override { return nullptr; }
-
-    void resetRegionOfInterest() override {}
-
     // Set the angular axis. A SpecularDetector1D can only have a single axis
     void setAxis(const IAxis& axis);
 
diff --git a/Device/Instrument/CoordSystem2D.cpp b/Device/Instrument/CoordSystem2D.cpp
index 20668ae5ad6cae0e4a073402ab1784aa71a0ac48..256ce56ee5eba253c555af9115e69c9a5546ecb9 100644
--- a/Device/Instrument/CoordSystem2D.cpp
+++ b/Device/Instrument/CoordSystem2D.cpp
@@ -13,14 +13,13 @@
 //  ************************************************************************************************
 
 #include "Device/Instrument/CoordSystem2D.h"
+#include "Base/Axis/FixedBinAxis.h"
 #include "Base/Const/Units.h"
 #include "Base/Math/Constants.h"
 #include "Base/Pixel/RectangularPixel.h"
+#include "Base/Utils/Assert.h"
 #include "Device/Beam/Beam.h"
 #include "Device/Coord/AxisNames.h"
-#include "Device/Detector/RectangularDetector.h"
-#include "Device/Detector/SphericalDetector.h"
-#include "Device/ProDetector/RegionOfInterest.h"
 #include <algorithm>
 #include <cmath>
 #include <stdexcept>
@@ -110,31 +109,24 @@ std::unique_ptr<const IAxis> CoordSystem2D::createConvertedAxis(size_t i_axis,
     return std::make_unique<FixedBinAxis>(axis_name, axis_size, min, max);
 }
 
-void CoordSystem2D::addDetectorAxis(const IDetector& detector, size_t i_axis)
+void CoordSystem2D::addAxis(const IAxis& axis)
 {
-    const auto& axis = detector.axis(i_axis);
-    const auto* roi = detector.regionOfInterest();
-    const auto& axis_name = axisName(i_axis);
-    if (!roi) {
-        addAxisData(axis_name, axis.lowerBound(), axis.upperBound(), defaultUnits(), axis.size());
-        return;
-    }
-    auto roi_axis = roi->clipAxisToRoi(i_axis, axis);
-    addAxisData(axis_name, roi_axis->lowerBound(), roi_axis->upperBound(), defaultUnits(),
-                roi_axis->size());
+    const size_t iNewAxis = m_axes.size();
+    addAxisData(axisName(iNewAxis), axis.lowerBound(), axis.upperBound(), defaultUnits(),
+                axis.size());
 }
 
-
 //  ************************************************************************************************
 //  class SphericalCoords
 //  ************************************************************************************************
 
-SphericalCoords::SphericalCoords(const SphericalDetector& detector, const Beam& beam)
+SphericalCoords::SphericalCoords(const CloneableVector<IAxis>& axes, const Beam& beam)
     : CoordSystem2D(beam)
 {
-    ASSERT(detector.dimension() == 2);
-    addDetectorAxis(detector, 0);
-    addDetectorAxis(detector, 1);
+    ASSERT(axes.size() == 2);
+
+    addAxis(*axes[0]);
+    addAxis(*axes[1]);
 }
 
 SphericalCoords::SphericalCoords(const SphericalCoords& other) : CoordSystem2D(other) {}
@@ -201,13 +193,15 @@ std::vector<std::map<Axes::Coords, std::string>> SphericalCoords::createNameMaps
 //  class ImageCoords
 //  ************************************************************************************************
 
-ImageCoords::ImageCoords(const RectangularDetector& detector, const Beam& beam)
+ImageCoords::ImageCoords(const CloneableVector<IAxis>& axes,
+                         const RectangularPixel& regionOfInterestPixel, const Beam& beam)
     : CoordSystem2D(beam)
 {
-    ASSERT(detector.dimension() == 2);
-    addDetectorAxis(detector, 0);
-    addDetectorAxis(detector, 1);
-    m_detector_pixel.reset(detector.regionOfInterestPixel());
+    ASSERT(axes.size() == 2);
+
+    addAxis(*axes[0]);
+    addAxis(*axes[1]);
+    m_detector_pixel.reset(regionOfInterestPixel.clone());
 }
 
 ImageCoords::ImageCoords(const ImageCoords& other)
@@ -278,14 +272,14 @@ std::vector<std::map<Axes::Coords, std::string>> ImageCoords::createNameMaps() c
 //  class OffSpecularCoordinates
 //  ************************************************************************************************
 
-OffSpecularCoordinates::OffSpecularCoordinates(const IDetector2D& detector, const Beam& beam,
-                                               const IAxis& alpha_axis)
+OffSpecularCoordinates::OffSpecularCoordinates(const Beam& beam, const IAxis& alpha_axis,
+                                               double alpha_f_min, double alpha_f_max, size_t nbins)
     : CoordSystem2D(beam)
 {
-    ASSERT(detector.dimension() == 2);
     addAxisData(axisName(0), alpha_axis.lowerBound(), alpha_axis.upperBound(), defaultUnits(),
                 alpha_axis.size());
-    addDetectorYAxis(detector);
+
+    addAxisData(axisName(1), alpha_f_min, alpha_f_max, defaultUnits(), nbins);
 }
 
 OffSpecularCoordinates::OffSpecularCoordinates(const OffSpecularCoordinates& other)
@@ -293,7 +287,28 @@ OffSpecularCoordinates::OffSpecularCoordinates(const OffSpecularCoordinates& oth
 {
 }
 
-OffSpecularCoordinates::~OffSpecularCoordinates() = default;
+OffSpecularCoordinates*
+OffSpecularCoordinates::createForRectangularDetector(const Beam& beam, const IAxis& alphaAxis,
+                                                     const RectangularPixel& detectorPixel,
+                                                     const IAxis& yAxis)
+{
+    const auto k00 = detectorPixel.getPosition(0.0, 0.0);
+    const auto k01 = detectorPixel.getPosition(0.0, 1.0);
+    const double alpha_f_min = M_PI_2 - k00.theta();
+    const double alpha_f_max = M_PI_2 - k01.theta();
+
+    return new OffSpecularCoordinates(beam, alphaAxis, alpha_f_min, alpha_f_max, yAxis.size());
+}
+
+OffSpecularCoordinates* OffSpecularCoordinates::createForSphericalDetector(const Beam& beam,
+                                                                           const IAxis& alphaAxis,
+                                                                           const IAxis& yAxis)
+{
+    const double alpha_f_min = yAxis.lowerBound();
+    const double alpha_f_max = yAxis.upperBound();
+
+    return new OffSpecularCoordinates(beam, alphaAxis, alpha_f_min, alpha_f_max, yAxis.size());
+}
 
 OffSpecularCoordinates* OffSpecularCoordinates::clone() const
 {
@@ -322,34 +337,6 @@ std::vector<std::map<Axes::Coords, std::string>> OffSpecularCoordinates::createN
     return {AxisNames::offSpecularAxis0, AxisNames::offSpecularAxis1};
 }
 
-void OffSpecularCoordinates::addDetectorYAxis(const IDetector2D& detector)
-{
-    const auto& axis = detector.axis(1);
-    const auto* roi = detector.regionOfInterest();
-    const auto& axis_name = axisName(1);
-    std::unique_ptr<IAxis> new_axis;
-    if (roi)
-        new_axis = roi->clipAxisToRoi(1, axis);
-    else
-        new_axis.reset(axis.clone());
-    ASSERT(new_axis);
-
-    if (const auto* rect_det = dynamic_cast<const RectangularDetector*>(&detector)) {
-        std::unique_ptr<RectangularPixel> det_pixel(rect_det->regionOfInterestPixel());
-        const auto k00 = det_pixel->getPosition(0.0, 0.0);
-        const auto k01 = det_pixel->getPosition(0.0, 1.0);
-        const double alpha_f_min = M_PI_2 - k00.theta();
-        const double alpha_f_max = M_PI_2 - k01.theta();
-        addAxisData(axis_name, alpha_f_min, alpha_f_max, defaultUnits(), new_axis->size());
-    } else if (dynamic_cast<const SphericalDetector*>(&detector)) {
-        const double alpha_f_min = new_axis->lowerBound();
-        const double alpha_f_max = new_axis->upperBound();
-        addAxisData(axis_name, alpha_f_min, alpha_f_max, defaultUnits(), new_axis->size());
-    } else
-        ASSERT(0);
-}
-
-
 //  ************************************************************************************************
 //  class DepthProbeCoordinates
 //  ************************************************************************************************
diff --git a/Device/Instrument/CoordSystem2D.h b/Device/Instrument/CoordSystem2D.h
index 56c8ac07cba983b0345cb53282e0dc21e8306154..d233aaad15f2426a3320b42a95ce6efdb560d404 100644
--- a/Device/Instrument/CoordSystem2D.h
+++ b/Device/Instrument/CoordSystem2D.h
@@ -20,15 +20,12 @@
 #ifndef BORNAGAIN_DEVICE_INSTRUMENT_COORDSYSTEM2D_H
 #define BORNAGAIN_DEVICE_INSTRUMENT_COORDSYSTEM2D_H
 
+#include "Base/Types/CloneableVector.h"
 #include "Base/Vector/Vectors3D.h"
 #include "Device/Coord/ICoordSystem.h"
 
 class Beam;
-class IDetector;
-class IDetector2D;
-class RectangularDetector;
 class RectangularPixel;
-class SphericalDetector;
 
 //! Interface for objects that provide axis translations to different units for IDetector objects
 //! @ingroup simulation_internal
@@ -51,9 +48,12 @@ public:
     std::unique_ptr<const IAxis> createConvertedAxis(size_t i_axis,
                                                      Axes::Coords units) const override;
 
+    //! Adds a copy of the given axis to the axis system.
+    //! Order of adding defines position of axis (x-axis, y-axis, ...)
+    void addAxis(const IAxis& axis);
+
 protected:
     CoordSystem2D(const CoordSystem2D& other);
-    void addDetectorAxis(const IDetector& detector, size_t i_axis);
 
     void addAxisData(std::string name, double min, double max, Axes::Coords default_units,
                      size_t nbins);
@@ -83,7 +83,7 @@ private:
 class SphericalCoords : public CoordSystem2D {
 public:
     SphericalCoords() = delete;
-    SphericalCoords(const SphericalDetector& detector, const Beam& beam);
+    SphericalCoords(const CloneableVector<IAxis>& axes, const Beam& beam);
     ~SphericalCoords() override;
 
     SphericalCoords* clone() const override;
@@ -107,7 +107,8 @@ private:
 class ImageCoords : public CoordSystem2D {
 public:
     ImageCoords() = delete;
-    ImageCoords(const RectangularDetector& detector, const Beam& beam);
+    ImageCoords(const CloneableVector<IAxis>& axes, const RectangularPixel& regionOfInterestPixel,
+                const Beam& beam);
     ~ImageCoords() override;
 
     ImageCoords* clone() const override;
@@ -133,20 +134,24 @@ private:
 
 class OffSpecularCoordinates : public CoordSystem2D {
 public:
-    OffSpecularCoordinates() = delete;
-    OffSpecularCoordinates(const IDetector2D& detector, const Beam& beam, const IAxis& alpha_axis);
-    ~OffSpecularCoordinates() override;
+    static OffSpecularCoordinates*
+    createForRectangularDetector(const Beam& beam, const IAxis& alphaAxis,
+                                 const RectangularPixel& detectorPixel, const IAxis& yAxis);
+
+    static OffSpecularCoordinates*
+    createForSphericalDetector(const Beam& beam, const IAxis& alphaAxis, const IAxis& yAxis);
 
     OffSpecularCoordinates* clone() const override;
 
     Axes::Coords defaultUnits() const override;
 
 private:
+    OffSpecularCoordinates() = delete;
+    OffSpecularCoordinates(const Beam& beam, const IAxis& alphaAxis, double alpha_f_min,
+                           double alpha_f_max, size_t nbins);
     OffSpecularCoordinates(const OffSpecularCoordinates& other); //!< used by clone()
     double calculateValue(size_t i_axis, Axes::Coords units, double value) const override;
     std::vector<std::map<Axes::Coords, std::string>> createNameMaps() const override;
-
-    void addDetectorYAxis(const IDetector2D& detector);
 };
 
 
diff --git a/Device/Instrument/Instrument.cpp b/Device/Instrument/Instrument.cpp
index fa81d528d906019d34d5304555ba217ea7c47019..7895afa3098182cdf506efd4c4fc67ccde44f919 100644
--- a/Device/Instrument/Instrument.cpp
+++ b/Device/Instrument/Instrument.cpp
@@ -13,6 +13,7 @@
 //  ************************************************************************************************
 
 #include "Device/Instrument/Instrument.h"
+#include "Base/Pixel/RectangularPixel.h"
 #include "Device/Detector/RectangularDetector.h"
 #include "Device/Detector/SphericalDetector.h"
 #include "Device/Histo/Histogram2D.h"
@@ -118,10 +119,11 @@ CoordSystem2D* Instrument::createScatteringCoords() const
 
     // TODO: use virtual fcts in detector classes instead of dynamic casts here
     if (const auto* const det = dynamic_cast<const SphericalDetector*>(detector))
-        return new SphericalCoords(*det, beam());
-
-    if (const auto* const det = dynamic_cast<const RectangularDetector*>(detector))
-        return new ImageCoords(*det, beam());
+        return new SphericalCoords(detector->axesClippedToRegionOfInterest(), beam());
 
+    if (const auto* const det = dynamic_cast<const RectangularDetector*>(detector)) {
+        std::unique_ptr<RectangularPixel> roiPixel(det->regionOfInterestPixel());
+        return new ImageCoords(detector->axesClippedToRegionOfInterest(), *roiPixel, beam());
+    }
     ASSERT(0);
 }
diff --git a/Device/ProDetector/RegionOfInterest.cpp b/Device/ProDetector/RegionOfInterest.cpp
deleted file mode 100644
index a6837047bcdcde6e9aec6afd96383043938db31e..0000000000000000000000000000000000000000
--- a/Device/ProDetector/RegionOfInterest.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      Device/ProDetector/RegionOfInterest.cpp
-//! @brief     Implements class RegionOfInterest.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#include "Device/ProDetector/RegionOfInterest.h"
-#include "Base/Axis/IAxis.h"
-#include "Base/Utils/Assert.h"
-#include "Device/Data/OutputData.h"
-#include "Device/Mask/Rectangle.h"
-
-RegionOfInterest::RegionOfInterest(const CloneableVector<IAxis>& axes, double xlow, double ylow,
-                                   double xup, double yup)
-    : RegionOfInterest(xlow, ylow, xup, yup)
-{
-    ASSERT(axes.size() == 2);
-    initFrom(*axes[0], *axes[1]);
-}
-
-RegionOfInterest::RegionOfInterest(double xlow, double ylow, double xup, double yup)
-    : m_rectangle(new Rectangle(xlow, ylow, xup, yup))
-    , m_ax1(0)
-    , m_ay1(0)
-    , m_ax2(0)
-    , m_ay2(0)
-    , m_glob_index0(0)
-{
-}
-
-RegionOfInterest* RegionOfInterest::clone() const
-{
-    return new RegionOfInterest(*this);
-}
-
-RegionOfInterest::~RegionOfInterest() = default;
-
-RegionOfInterest::RegionOfInterest(const RegionOfInterest& other)
-    : ICloneable()
-    , m_rectangle(other.m_rectangle->clone())
-    , m_ax1(other.m_ax1)
-    , m_ay1(other.m_ay1)
-    , m_ax2(other.m_ax2)
-    , m_ay2(other.m_ay2)
-    , m_glob_index0(other.m_glob_index0)
-    , m_detector_dims(other.m_detector_dims)
-    , m_roi_dims(other.m_roi_dims)
-{
-}
-
-double RegionOfInterest::getXlow() const
-{
-    return m_rectangle->getXlow();
-}
-
-double RegionOfInterest::getYlow() const
-{
-    return m_rectangle->getYlow();
-}
-
-double RegionOfInterest::getXup() const
-{
-    return m_rectangle->getXup();
-}
-
-double RegionOfInterest::getYup() const
-{
-    return m_rectangle->getYup();
-}
-
-size_t RegionOfInterest::detectorIndex(size_t roiIndex) const
-{
-    return m_glob_index0 + ycoord(roiIndex, m_roi_dims)
-           + xcoord(roiIndex, m_roi_dims) * m_detector_dims[1];
-}
-
-size_t RegionOfInterest::roiIndex(size_t globalIndex) const
-{
-    size_t ny = ycoord(globalIndex, m_detector_dims);
-    if (ny < m_ay1 || ny > m_ay2)
-        throw std::runtime_error("RegionOfInterest::roiIndex() -> Error.");
-
-    size_t nx = xcoord(globalIndex, m_detector_dims);
-    if (nx < m_ax1 || nx > m_ax2)
-        throw std::runtime_error("RegionOfInterest::roiIndex() -> Error.");
-
-    return ny - m_ay1 + (nx - m_ax1) * m_roi_dims[1];
-}
-
-size_t RegionOfInterest::roiSize() const
-{
-    return m_roi_dims[0] * m_roi_dims[1];
-}
-
-size_t RegionOfInterest::detectorSize() const
-{
-    return m_detector_dims[0] * m_detector_dims[1];
-}
-
-bool RegionOfInterest::isInROI(size_t detectorIndex) const
-{
-    size_t ny = ycoord(detectorIndex, m_detector_dims);
-    if (ny < m_ay1 || ny > m_ay2)
-        return false;
-    size_t nx = xcoord(detectorIndex, m_detector_dims);
-    if (nx < m_ax1 || nx > m_ax2)
-        return false;
-    return true;
-}
-
-std::unique_ptr<IAxis> RegionOfInterest::clipAxisToRoi(size_t axis_index, const IAxis& axis) const
-{
-    size_t nbin1 = (axis_index == 0 ? m_ax1 : m_ay1);
-    size_t nbin2 = (axis_index == 0 ? m_ax2 : m_ay2);
-    return std::unique_ptr<IAxis>(new FixedBinAxis(
-        axis.getName(), nbin2 - nbin1 + 1, axis.bin(nbin1).m_lower, axis.bin(nbin2).m_upper));
-}
-
-void RegionOfInterest::initFrom(const IAxis& x_axis, const IAxis& y_axis)
-{
-    m_detector_dims.push_back(x_axis.size());
-    m_detector_dims.push_back(y_axis.size());
-
-    m_ax1 = x_axis.findClosestIndex(getXlow());
-    m_ax2 = x_axis.findClosestIndex(getXup());
-    m_ay1 = y_axis.findClosestIndex(getYlow());
-    m_ay2 = y_axis.findClosestIndex(getYup());
-
-    m_roi_dims.push_back(m_ax2 - m_ax1 + 1);
-    m_roi_dims.push_back(m_ay2 - m_ay1 + 1);
-
-    m_glob_index0 = m_ay1 + m_ax1 * m_detector_dims[1];
-}
diff --git a/Device/ProDetector/RegionOfInterest.h b/Device/ProDetector/RegionOfInterest.h
deleted file mode 100644
index 38c2978bb0a81a12e4978e505f233f3e7bb3e6ba..0000000000000000000000000000000000000000
--- a/Device/ProDetector/RegionOfInterest.h
+++ /dev/null
@@ -1,94 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      Device/ProDetector/RegionOfInterest.h
-//! @brief     Defines class RegionOfInterest.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifdef SWIG
-#error no need to expose this header to Swig
-#endif
-
-#ifndef USER_API
-#ifndef BORNAGAIN_DEVICE_PRODETECTOR_REGIONOFINTEREST_H
-#define BORNAGAIN_DEVICE_PRODETECTOR_REGIONOFINTEREST_H
-
-#include "Base/Types/CloneableVector.h"
-#include "Base/Types/ICloneable.h"
-#include <memory>
-#include <vector>
-
-class Rectangle;
-class IAxis;
-template <class T> class OutputData;
-
-//! Defines rectangular area for the detector which will be simulated/fitted.
-//! @ingroup detector
-
-class RegionOfInterest : public ICloneable {
-public:
-    RegionOfInterest(const CloneableVector<IAxis>& axes, double xlow, double ylow, double xup,
-                     double yup);
-
-    RegionOfInterest& operator=(const RegionOfInterest& other) = delete;
-    RegionOfInterest* clone() const;
-    ~RegionOfInterest();
-
-    double getXlow() const;
-    double getYlow() const;
-    double getXup() const;
-    double getYup() const;
-
-    //! Converts roi index to the detector index.
-    size_t detectorIndex(size_t roiIndex) const;
-
-    //! Converts global detector index to ROI index.
-    size_t roiIndex(size_t detectorIndex) const;
-
-    //! Number of detector bins in ROI area
-    size_t roiSize() const;
-
-    //! Number of detector bins.
-    size_t detectorSize() const;
-
-    bool isInROI(size_t detectorIndex) const;
-
-    std::unique_ptr<IAxis> clipAxisToRoi(size_t axis_index, const IAxis& axis) const;
-
-private:
-    RegionOfInterest(double xlow, double ylow, double xup, double yup);
-    RegionOfInterest(const RegionOfInterest& other);
-
-    size_t xcoord(size_t index, const std::vector<size_t>& dims) const;
-    size_t ycoord(size_t index, const std::vector<size_t>& dims) const;
-
-    void initFrom(const IAxis& x_axis, const IAxis& y_axis);
-
-    std::unique_ptr<Rectangle> m_rectangle;
-    //! Number of bins on detector axes corresponding to roi-rectangle.
-    size_t m_ax1, m_ay1, m_ax2, m_ay2;
-    //! Detector global index corresponding to the lower left corner of ROI
-    size_t m_glob_index0;
-    std::vector<size_t> m_detector_dims;
-    std::vector<size_t> m_roi_dims;
-};
-
-inline size_t RegionOfInterest::xcoord(size_t index, const std::vector<size_t>& dims) const
-{
-    return index / dims[1] % dims[0];
-}
-
-inline size_t RegionOfInterest::ycoord(size_t index, const std::vector<size_t>& dims) const
-{
-    return index % dims[1];
-}
-
-#endif // BORNAGAIN_DEVICE_PRODETECTOR_REGIONOFINTEREST_H
-#endif // USER_API
diff --git a/GUI/Models/InstrumentItems.cpp b/GUI/Models/InstrumentItems.cpp
index 01acf7118c83035b3a5f9c17d2dfa70664541042..cd2f50eb6c64d23b7095202a3db8e46c7f0c71ad 100644
--- a/GUI/Models/InstrumentItems.cpp
+++ b/GUI/Models/InstrumentItems.cpp
@@ -14,8 +14,11 @@
 
 #include "GUI/Models/InstrumentItems.h"
 #include "Base/Const/Units.h"
+#include "Base/Pixel/RectangularPixel.h"
 #include "Core/Simulation/DepthProbeSimulation.h"
 #include "Device/Coord/CoordSystem1D.h"
+#include "Device/Detector/RectangularDetector.h"
+#include "Device/Detector/SphericalDetector.h"
 #include "Device/Instrument/CoordSystem2D.h"
 #include "Device/Instrument/Instrument.h"
 #include "GUI/Models/BackgroundItems.h"
@@ -168,8 +171,7 @@ bool SpecularInstrumentItem::alignedWith(const RealDataItem* item) const
 {
     const QString native_units = item->nativeDataUnits();
     if (native_units == "nbins") {
-        return beamItem()->currentInclinationAxisItem()->modelType()
-               == BasicAxisItem::M_TYPE
+        return beamItem()->currentInclinationAxisItem()->modelType() == BasicAxisItem::M_TYPE
                && shape() == item->shape();
     } else {
         auto axis_item = dynamic_cast<PointwiseAxisItem*>(beamItem()->currentInclinationAxisItem());
@@ -348,10 +350,23 @@ ICoordSystem* OffSpecularInstrumentItem::createCoordSystem() const
 {
     const auto instrument = createInstrument();
     instrument->initDetector();
-    auto axis_item = item<BasicAxisItem>(OffSpecularInstrumentItem::P_ALPHA_AXIS);
+    const auto axis_item = item<BasicAxisItem>(OffSpecularInstrumentItem::P_ALPHA_AXIS);
     const auto detector2d = dynamic_cast<const IDetector2D*>(instrument->getDetector());
-    return new OffSpecularCoordinates(*detector2d, instrument->beam(),
-                                      *axis_item->createAxis(Units::deg));
+    const auto axes = detector2d->axesClippedToRegionOfInterest();
+    ASSERT(axes.size() == 2);
+    const IAxis& yAxis = *axes[1];
+    const Beam& beam = instrument->beam();
+    const auto alphaAxis = axis_item->createAxis(Units::deg);
+
+    if (const auto* rectDetector = dynamic_cast<const RectangularDetector*>(detector2d)) {
+        std::unique_ptr<RectangularPixel> detectorPixel(rectDetector->regionOfInterestPixel());
+        return OffSpecularCoordinates::createForRectangularDetector(beam, *alphaAxis,
+                                                                    *detectorPixel, yAxis);
+    } else if (dynamic_cast<const SphericalDetector*>(detector2d))
+        return OffSpecularCoordinates::createForSphericalDetector(beam, *alphaAxis, yAxis);
+
+    ASSERT(0);
+    return nullptr;
 }
 
 //  ************************************************************************************************
@@ -378,10 +393,8 @@ DepthProbeInstrumentItem::DepthProbeInstrumentItem() : InstrumentItem("DepthProb
     axis->setUpperBound(100.0);
     axis->titleItem()->setVisible(false);
     axis->binsItem()->setToolTip("Number of points in scan across sample bulk");
-    axis->lowerBoundItem()
-        ->setToolTip("Starting value below sample horizont in nm");
-    axis->upperBoundItem()
-        ->setToolTip("Ending value above sample horizont in nm");
+    axis->lowerBoundItem()->setToolTip("Starting value below sample horizont in nm");
+    axis->upperBoundItem()->setToolTip("Ending value above sample horizont in nm");
 }
 
 SpecularBeamItem* DepthProbeInstrumentItem::beamItem() const
@@ -424,14 +437,11 @@ std::unique_ptr<DepthProbeSimulation> DepthProbeInstrumentItem::createSimulation
     auto depthAxis = depthAxisItem->createAxis(1.0);
     simulation->setZSpan(depthAxis->size(), depthAxis->lowerBound(), depthAxis->upperBound());
 
-    TransformToDomain::setBeamDistribution(
-        ParameterDistribution::BeamWavelength, *beamItem()->wavelengthItem(),
-        *simulation.get());
+    TransformToDomain::setBeamDistribution(ParameterDistribution::BeamWavelength,
+                                           *beamItem()->wavelengthItem(), *simulation.get());
 
-    TransformToDomain::setBeamDistribution(
-        ParameterDistribution::BeamInclinationAngle,
-        *beamItem()->inclinationAngleItem(),
-        *simulation.get());
+    TransformToDomain::setBeamDistribution(ParameterDistribution::BeamInclinationAngle,
+                                           *beamItem()->inclinationAngleItem(), *simulation.get());
 
     return simulation;
 }
diff --git a/GUI/Models/MaskItems.cpp b/GUI/Models/MaskItems.cpp
index a0bc3b6e73fd50d8276de915922125664e6698f5..cce94698b92cdb2b44e34a5e2c3eca48d2802fa3 100644
--- a/GUI/Models/MaskItems.cpp
+++ b/GUI/Models/MaskItems.cpp
@@ -82,6 +82,13 @@ RegionOfInterestItem::RegionOfInterestItem() : RectangleItem("RegionOfInterest")
     setItemValue(P_MASK_VALUE, false);
 }
 
+std::unique_ptr<IShape2D> RegionOfInterestItem::createShape(double scale) const
+{
+    auto shape = RectangleItem::createShape(scale);
+    static_cast<Rectangle*>(shape.get())->setInverted(true);
+    return shape;
+}
+
 /* ------------------------------------------------------------------------- */
 
 const QString PolygonPointItem::P_POSX = "X position";
diff --git a/GUI/Models/MaskItems.h b/GUI/Models/MaskItems.h
index 6657d03f889149abfb5d65abe144eda4b6589088..0db27479e5fc3186e67f6bbeebb0e5e340b3ba4a 100644
--- a/GUI/Models/MaskItems.h
+++ b/GUI/Models/MaskItems.h
@@ -49,6 +49,7 @@ public:
 class BA_CORE_API_ RegionOfInterestItem : public RectangleItem {
 public:
     RegionOfInterestItem();
+    virtual std::unique_ptr<IShape2D> createShape(double scale) const;
 };
 
 class BA_CORE_API_ PolygonPointItem : public SessionItem {
diff --git a/GUI/Models/TransformFromDomain.cpp b/GUI/Models/TransformFromDomain.cpp
index 462f15e5f74859ed4f555b6bca0430a7bd2d5a7b..12f30eca153a79450ef6b35c7adf87d4963a12c7 100644
--- a/GUI/Models/TransformFromDomain.cpp
+++ b/GUI/Models/TransformFromDomain.cpp
@@ -28,7 +28,6 @@
 #include "Device/Mask/Line.h"
 #include "Device/Mask/Polygon.h"
 #include "Device/Mask/Rectangle.h"
-#include "Device/ProDetector/RegionOfInterest.h"
 #include "Device/Resolution/ConvolutionDetectorResolution.h"
 #include "Device/Resolution/ResolutionFunction2DGaussian.h"
 #include "Device/Resolution/ScanResolution.h"
@@ -439,10 +438,10 @@ void TransformFromDomain::setDetectorMasks(DetectorItem* detector_item,
                                            const ISimulation& simulation)
 {
     const IDetector* detector = simulation.instrument().getDetector();
-    if ((detector->detectorMask() && detector->detectorMask()->numberOfMasks())
-        || detector->regionOfInterest()) { // #baROI refactor: hasRegionOfInterest()
-        detector_item->createMaskContainer();
+    if ((detector->detectorMask() && detector->detectorMask()->hasMasks())
+        || detector->hasExplicitRegionOfInterest()) {
 
+        detector_item->createMaskContainer();
         double scale(1.0);
         if (detector_item->modelType() == "SphericalDetector")
             scale = 1. / Units::deg;
@@ -525,14 +524,15 @@ void TransformFromDomain::setMaskContainer(MaskContainerItem* container_item,
         }
     }
 
-    if (detector.regionOfInterest()) {
+    if (detector.hasExplicitRegionOfInterest()) {
+        const auto xBounds = detector.regionOfInterestBounds(0);
+        const auto yBounds = detector.regionOfInterestBounds(1);
+
         RegionOfInterestItem* roiItem = new RegionOfInterestItem();
-        roiItem->setItemValue(RectangleItem::P_XLOW,
-                              scale * detector.regionOfInterest()->getXlow());
-        roiItem->setItemValue(RectangleItem::P_YLOW,
-                              scale * detector.regionOfInterest()->getYlow());
-        roiItem->setItemValue(RectangleItem::P_XUP, scale * detector.regionOfInterest()->getXup());
-        roiItem->setItemValue(RectangleItem::P_YUP, scale * detector.regionOfInterest()->getYup());
+        roiItem->setItemValue(RectangleItem::P_XLOW, scale * xBounds.first);
+        roiItem->setItemValue(RectangleItem::P_YLOW, scale * yBounds.first);
+        roiItem->setItemValue(RectangleItem::P_XUP, scale * xBounds.second);
+        roiItem->setItemValue(RectangleItem::P_YUP, scale * yBounds.second);
         container_item->insertItem(-1, roiItem);
     }
 }
diff --git a/GUI/Views/MaskWidgets/MaskResultsPresenter.cpp b/GUI/Views/MaskWidgets/MaskResultsPresenter.cpp
index 8276dd8166440a087f5b86ef92ca36f5f4340a74..9438e39b6615cecd62d869728dca026fffed2662 100644
--- a/GUI/Views/MaskWidgets/MaskResultsPresenter.cpp
+++ b/GUI/Views/MaskWidgets/MaskResultsPresenter.cpp
@@ -14,7 +14,6 @@
 
 #include "GUI/Views/MaskWidgets/MaskResultsPresenter.h"
 #include "Device/ProDetector/DetectorMask.h"
-#include "Device/ProDetector/RegionOfInterest.h"
 #include "GUI/Models/IntensityDataItem.h"
 #include "GUI/Models/MaskItems.h"
 #include "GUI/Models/SessionModel.h"
@@ -87,18 +86,13 @@ void MaskResultsPresenter::backup_data()
 OutputData<double>* MaskResultsPresenter::createMaskPresentation() const
 {
     // Requesting mask information
-    std::unique_ptr<RegionOfInterest> roi;
+    std::unique_ptr<IShape2D> roi;
     DetectorMask detectorMask;
     for (int i_row = m_maskModel->rowCount(m_maskContainerIndex); i_row > 0; --i_row) {
         QModelIndex itemIndex = m_maskModel->index(i_row - 1, 0, m_maskContainerIndex);
         if (MaskItem* maskItem = dynamic_cast<MaskItem*>(m_maskModel->itemForIndex(itemIndex))) {
             if (maskItem->modelType() == "RegionOfInterest") {
-                double xlow = maskItem->getItemValue(RectangleItem::P_XLOW).toDouble();
-                double ylow = maskItem->getItemValue(RectangleItem::P_YLOW).toDouble();
-                double xup = maskItem->getItemValue(RectangleItem::P_XUP).toDouble();
-                double yup = maskItem->getItemValue(RectangleItem::P_YUP).toDouble();
-                roi.reset(new RegionOfInterest(m_intensityDataItem->getOutputData()->axes(), xlow,
-                                               ylow, xup, yup));
+                roi = maskItem->createShape();
             } else {
                 std::unique_ptr<IShape2D> shape(maskItem->createShape());
                 bool mask_value = maskItem->getItemValue(MaskItem::P_MASK_VALUE).toBool();
@@ -107,14 +101,18 @@ OutputData<double>* MaskResultsPresenter::createMaskPresentation() const
         }
     }
 
-    if (!detectorMask.hasMasks() && !roi)
-        return 0;
+    // ROI mask has to be the last one, it can not be "unmasked" by other shapes
+    if (roi)
+        detectorMask.addMask(*roi, true);
+
+    if (!detectorMask.hasMasks())
+        return nullptr;
 
     OutputData<double>* result = m_intensityDataItem->getOutputData()->clone();
     detectorMask.initMaskData(result->axes());
 
     for (size_t i = 0; i < result->getAllocatedSize(); ++i)
-        if (detectorMask.isMasked(i) || (roi && !roi->isInROI(i)))
+        if (detectorMask.isMasked(i))
             (*result)[i] = 0.0;
 
     return result;
diff --git a/Tests/UnitTests/Core/Instrument/OffSpecularConverterTest.cpp b/Tests/UnitTests/Core/Instrument/OffSpecularConverterTest.cpp
index 1ffad75272ea0cd47ea6c4e648a361826227680a..36d5faa094bb71a7ff265f9a4b2a3b90359a7958 100644
--- a/Tests/UnitTests/Core/Instrument/OffSpecularConverterTest.cpp
+++ b/Tests/UnitTests/Core/Instrument/OffSpecularConverterTest.cpp
@@ -8,6 +8,14 @@ class OffSpecularCoordinatesTest : public ::testing::Test {
 public:
     OffSpecularCoordinatesTest();
 
+    OffSpecularCoordinates* createCoords()
+    {
+        const auto axes = m_detector.axesClippedToRegionOfInterest();
+        const IAxis& yAxis = *axes[1];
+
+        return OffSpecularCoordinates::createForSphericalDetector(m_beam, m_alpha_i_axis, yAxis);
+    }
+
 protected:
     SphericalDetector m_detector;
     FixedBinAxis m_alpha_i_axis;
@@ -23,61 +31,61 @@ OffSpecularCoordinatesTest::OffSpecularCoordinatesTest()
 
 TEST_F(OffSpecularCoordinatesTest, OffSpecularCoordinates)
 {
-    OffSpecularCoordinates converter(m_detector, m_beam, m_alpha_i_axis);
-
-    EXPECT_EQ(converter.dimension(), 2u);
-
-    EXPECT_DOUBLE_EQ(converter.calculateMin(0, Axes::Coords::UNDEFINED), 0.0);
-    EXPECT_DOUBLE_EQ(converter.calculateMin(0, Axes::Coords::NBINS), 0.0);
-    EXPECT_DOUBLE_EQ(converter.calculateMin(0, Axes::Coords::RADIANS), 0.0);
-    EXPECT_DOUBLE_EQ(converter.calculateMin(0, Axes::Coords::DEGREES), 0.0);
-    EXPECT_THROW(converter.calculateMin(0, Axes::Coords::QSPACE), std::runtime_error);
-    EXPECT_THROW(converter.calculateMin(0, Axes::Coords::MM), std::runtime_error);
-
-    EXPECT_DOUBLE_EQ(converter.calculateMax(0, Axes::Coords::UNDEFINED), 7.0);
-    EXPECT_DOUBLE_EQ(converter.calculateMax(0, Axes::Coords::NBINS), 51.0);
-    EXPECT_DOUBLE_EQ(converter.calculateMax(0, Axes::Coords::RADIANS), Units::deg2rad(7.0));
-    EXPECT_DOUBLE_EQ(converter.calculateMax(0, Axes::Coords::DEGREES), 7.0);
-    EXPECT_THROW(converter.calculateMax(0, Axes::Coords::QSPACE), std::runtime_error);
-    EXPECT_THROW(converter.calculateMax(0, Axes::Coords::MM), std::runtime_error);
-
-    EXPECT_DOUBLE_EQ(converter.calculateMin(1, Axes::Coords::UNDEFINED), -2.0);
-    EXPECT_DOUBLE_EQ(converter.calculateMin(1, Axes::Coords::NBINS), 0.0);
-    EXPECT_DOUBLE_EQ(converter.calculateMin(1, Axes::Coords::RADIANS), Units::deg2rad(-2.0));
-    EXPECT_DOUBLE_EQ(converter.calculateMin(1, Axes::Coords::DEGREES), -2.0);
-    EXPECT_THROW(converter.calculateMin(1, Axes::Coords::QSPACE), std::runtime_error);
-    EXPECT_THROW(converter.calculateMin(1, Axes::Coords::MM), std::runtime_error);
-
-    EXPECT_DOUBLE_EQ(converter.calculateMax(1, Axes::Coords::UNDEFINED), Units::rad2deg(1.5));
-    EXPECT_DOUBLE_EQ(converter.calculateMax(1, Axes::Coords::NBINS), 70.0);
-    EXPECT_DOUBLE_EQ(converter.calculateMax(1, Axes::Coords::RADIANS), 1.5);
-    EXPECT_DOUBLE_EQ(converter.calculateMax(1, Axes::Coords::DEGREES), Units::rad2deg(1.5));
-    EXPECT_THROW(converter.calculateMax(1, Axes::Coords::QSPACE), std::runtime_error);
-    EXPECT_THROW(converter.calculateMax(1, Axes::Coords::MM), std::runtime_error);
-
-    EXPECT_THROW(converter.calculateMin(2, Axes::Coords::UNDEFINED), std::runtime_error);
-    EXPECT_THROW(converter.calculateMax(2, Axes::Coords::UNDEFINED), std::runtime_error);
-
-    auto axis = converter.createConvertedAxis(0, Axes::Coords::UNDEFINED);
+    std::unique_ptr<OffSpecularCoordinates> coords(createCoords());
+
+    EXPECT_EQ(coords->dimension(), 2u);
+
+    EXPECT_DOUBLE_EQ(coords->calculateMin(0, Axes::Coords::UNDEFINED), 0.0);
+    EXPECT_DOUBLE_EQ(coords->calculateMin(0, Axes::Coords::NBINS), 0.0);
+    EXPECT_DOUBLE_EQ(coords->calculateMin(0, Axes::Coords::RADIANS), 0.0);
+    EXPECT_DOUBLE_EQ(coords->calculateMin(0, Axes::Coords::DEGREES), 0.0);
+    EXPECT_THROW(coords->calculateMin(0, Axes::Coords::QSPACE), std::runtime_error);
+    EXPECT_THROW(coords->calculateMin(0, Axes::Coords::MM), std::runtime_error);
+
+    EXPECT_DOUBLE_EQ(coords->calculateMax(0, Axes::Coords::UNDEFINED), 7.0);
+    EXPECT_DOUBLE_EQ(coords->calculateMax(0, Axes::Coords::NBINS), 51.0);
+    EXPECT_DOUBLE_EQ(coords->calculateMax(0, Axes::Coords::RADIANS), Units::deg2rad(7.0));
+    EXPECT_DOUBLE_EQ(coords->calculateMax(0, Axes::Coords::DEGREES), 7.0);
+    EXPECT_THROW(coords->calculateMax(0, Axes::Coords::QSPACE), std::runtime_error);
+    EXPECT_THROW(coords->calculateMax(0, Axes::Coords::MM), std::runtime_error);
+
+    EXPECT_DOUBLE_EQ(coords->calculateMin(1, Axes::Coords::UNDEFINED), -2.0);
+    EXPECT_DOUBLE_EQ(coords->calculateMin(1, Axes::Coords::NBINS), 0.0);
+    EXPECT_DOUBLE_EQ(coords->calculateMin(1, Axes::Coords::RADIANS), Units::deg2rad(-2.0));
+    EXPECT_DOUBLE_EQ(coords->calculateMin(1, Axes::Coords::DEGREES), -2.0);
+    EXPECT_THROW(coords->calculateMin(1, Axes::Coords::QSPACE), std::runtime_error);
+    EXPECT_THROW(coords->calculateMin(1, Axes::Coords::MM), std::runtime_error);
+
+    EXPECT_DOUBLE_EQ(coords->calculateMax(1, Axes::Coords::UNDEFINED), Units::rad2deg(1.5));
+    EXPECT_DOUBLE_EQ(coords->calculateMax(1, Axes::Coords::NBINS), 70.0);
+    EXPECT_DOUBLE_EQ(coords->calculateMax(1, Axes::Coords::RADIANS), 1.5);
+    EXPECT_DOUBLE_EQ(coords->calculateMax(1, Axes::Coords::DEGREES), Units::rad2deg(1.5));
+    EXPECT_THROW(coords->calculateMax(1, Axes::Coords::QSPACE), std::runtime_error);
+    EXPECT_THROW(coords->calculateMax(1, Axes::Coords::MM), std::runtime_error);
+
+    EXPECT_THROW(coords->calculateMin(2, Axes::Coords::UNDEFINED), std::runtime_error);
+    EXPECT_THROW(coords->calculateMax(2, Axes::Coords::UNDEFINED), std::runtime_error);
+
+    auto axis = coords->createConvertedAxis(0, Axes::Coords::UNDEFINED);
     EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis.get()));
-    EXPECT_EQ(axis->size(), converter.axisSize(0));
-    EXPECT_EQ(axis->lowerBound(), converter.calculateMin(0, Axes::Coords::UNDEFINED));
-    EXPECT_EQ(axis->upperBound(), converter.calculateMax(0, Axes::Coords::UNDEFINED));
+    EXPECT_EQ(axis->size(), coords->axisSize(0));
+    EXPECT_EQ(axis->lowerBound(), coords->calculateMin(0, Axes::Coords::UNDEFINED));
+    EXPECT_EQ(axis->upperBound(), coords->calculateMax(0, Axes::Coords::UNDEFINED));
 
-    auto axis2 = converter.createConvertedAxis(1, Axes::Coords::RADIANS);
+    auto axis2 = coords->createConvertedAxis(1, Axes::Coords::RADIANS);
     EXPECT_TRUE(dynamic_cast<const FixedBinAxis*>(axis2.get()));
-    EXPECT_EQ(axis2->size(), converter.axisSize(1));
-    EXPECT_EQ(axis2->lowerBound(), converter.calculateMin(1, Axes::Coords::RADIANS));
-    EXPECT_EQ(axis2->upperBound(), converter.calculateMax(1, Axes::Coords::RADIANS));
+    EXPECT_EQ(axis2->size(), coords->axisSize(1));
+    EXPECT_EQ(axis2->lowerBound(), coords->calculateMin(1, Axes::Coords::RADIANS));
+    EXPECT_EQ(axis2->upperBound(), coords->calculateMax(1, Axes::Coords::RADIANS));
 
-    EXPECT_THROW(converter.createConvertedAxis(2, Axes::Coords::UNDEFINED), std::runtime_error);
-    EXPECT_THROW(converter.createConvertedAxis(1, Axes::Coords::QSPACE), std::runtime_error);
+    EXPECT_THROW(coords->createConvertedAxis(2, Axes::Coords::UNDEFINED), std::runtime_error);
+    EXPECT_THROW(coords->createConvertedAxis(1, Axes::Coords::QSPACE), std::runtime_error);
 }
 
 TEST_F(OffSpecularCoordinatesTest, OffSpecularCoordinatesClone)
 {
-    OffSpecularCoordinates converter(m_detector, m_beam, m_alpha_i_axis);
-    std::unique_ptr<OffSpecularCoordinates> P_clone(converter.clone());
+    std::unique_ptr<OffSpecularCoordinates> coords(createCoords());
+    std::unique_ptr<OffSpecularCoordinates> P_clone(coords->clone());
 
     EXPECT_EQ(P_clone->dimension(), 2u);
 
diff --git a/Tests/UnitTests/Core/Instrument/RectangularConverterTest.cpp b/Tests/UnitTests/Core/Instrument/RectangularConverterTest.cpp
index f3cfcbf42412f7813bc54003db7fc74f981e650c..799bc663e6ef9dcc031f79dcc90608bfa01fe7de 100644
--- a/Tests/UnitTests/Core/Instrument/RectangularConverterTest.cpp
+++ b/Tests/UnitTests/Core/Instrument/RectangularConverterTest.cpp
@@ -1,4 +1,5 @@
 #include "Base/Const/Units.h"
+#include "Base/Pixel/RectangularPixel.h"
 #include "Device/Beam/Beam.h"
 #include "Device/Detector/RectangularDetector.h"
 #include "Device/Instrument/CoordSystem2D.h"
@@ -40,7 +41,8 @@ ImageCoordsTest::ImageCoordsTest()
 
 TEST_F(ImageCoordsTest, ImageCoords)
 {
-    ImageCoords converter(m_detector, m_beam);
+    std::unique_ptr<RectangularPixel> roiPixel(m_detector.regionOfInterestPixel());
+    ImageCoords converter(m_detector.axesClippedToRegionOfInterest(), *roiPixel, m_beam);
 
     EXPECT_EQ(converter.dimension(), 2u);
 
@@ -98,7 +100,8 @@ TEST_F(ImageCoordsTest, ImageCoords)
 
 TEST_F(ImageCoordsTest, ImageCoordsClone)
 {
-    ImageCoords converter(m_detector, m_beam);
+    std::unique_ptr<RectangularPixel> roiPixel(m_detector.regionOfInterestPixel());
+    ImageCoords converter(m_detector.axesClippedToRegionOfInterest(), *roiPixel, m_beam);
     std::unique_ptr<ImageCoords> P_clone(converter.clone());
 
     EXPECT_EQ(P_clone->dimension(), 2u);
@@ -149,7 +152,8 @@ TEST_F(ImageCoordsTest, ImageCoordsWithROI)
     const double roi_ymax = 100; // ymax in roi will be 102 due to binning
 
     m_detector.setRegionOfInterest(roi_xmin, roi_ymin, roi_xmax, roi_ymax);
-    ImageCoords converter(m_detector, m_beam);
+    std::unique_ptr<RectangularPixel> roiPixel(m_detector.regionOfInterestPixel());
+    ImageCoords converter(m_detector.axesClippedToRegionOfInterest(), *roiPixel, m_beam);
 
     EXPECT_EQ(converter.calculateMin(0, Axes::Coords::UNDEFINED), 100);
     EXPECT_EQ(converter.calculateMax(0, Axes::Coords::UNDEFINED), 152);
diff --git a/Tests/UnitTests/Core/Instrument/SpecularDetector1DTest.cpp b/Tests/UnitTests/Core/Instrument/SpecularDetector1DTest.cpp
index 76d5289a97e3693ff0be611a5b2a4bd97635a01c..d17050c0c37f0834871ecf0666e78db9f331629d 100644
--- a/Tests/UnitTests/Core/Instrument/SpecularDetector1DTest.cpp
+++ b/Tests/UnitTests/Core/Instrument/SpecularDetector1DTest.cpp
@@ -16,7 +16,7 @@ TEST_F(SpecularDetectorTest, basicBehaviour)
 
     // masks, regions of interest, detector resolution
     EXPECT_EQ(nullptr, detector.detectorMask());
-    EXPECT_EQ(nullptr, detector.regionOfInterest());
+    EXPECT_FALSE(detector.hasExplicitRegionOfInterest());
     EXPECT_EQ(nullptr, detector.detectorResolution());
 
     // checking size and axis
diff --git a/Tests/UnitTests/Core/Instrument/SphericalConverterTest.cpp b/Tests/UnitTests/Core/Instrument/SphericalConverterTest.cpp
index d4e390f1d35014d9ec26e7f2489a4882ea8ba76a..62db703712cff9aee68717ea2d6affad38bb590e 100644
--- a/Tests/UnitTests/Core/Instrument/SphericalConverterTest.cpp
+++ b/Tests/UnitTests/Core/Instrument/SphericalConverterTest.cpp
@@ -28,7 +28,7 @@ SphericalCoordsTest::SphericalCoordsTest()
 
 TEST_F(SphericalCoordsTest, SphericalCoords)
 {
-    SphericalCoords converter(m_detector, m_beam);
+    SphericalCoords converter(m_detector.axesClippedToRegionOfInterest(), m_beam);
 
     EXPECT_EQ(converter.dimension(), 2u);
 
@@ -80,7 +80,7 @@ TEST_F(SphericalCoordsTest, SphericalCoords)
 
 TEST_F(SphericalCoordsTest, SphericalCoordsClone)
 {
-    SphericalCoords converter(m_detector, m_beam);
+    SphericalCoords converter(m_detector.axesClippedToRegionOfInterest(), m_beam);
     std::unique_ptr<SphericalCoords> P_clone(converter.clone());
 
     EXPECT_EQ(P_clone->dimension(), 2u);
diff --git a/Tests/UnitTests/Core/Instrument/SphericalDetectorTest.cpp b/Tests/UnitTests/Core/Instrument/SphericalDetectorTest.cpp
index 198c68aec9d5710a8197735792e8a8e7ad7b569e..47d1c982ac280b7ae457fa545a16995510a2ad4c 100644
--- a/Tests/UnitTests/Core/Instrument/SphericalDetectorTest.cpp
+++ b/Tests/UnitTests/Core/Instrument/SphericalDetectorTest.cpp
@@ -3,7 +3,6 @@
 #include "Device/Beam/Beam.h"
 #include "Device/Mask/Polygon.h"
 #include "Device/Mask/Rectangle.h"
-#include "Device/ProDetector/RegionOfInterest.h"
 #include "Device/Resolution/ConvolutionDetectorResolution.h"
 #include "Device/Resolution/ResolutionFunction2DGaussian.h"
 #include "Tests/GTestWrapper/google_test.h"
@@ -29,7 +28,7 @@ TEST_F(SphericalDetectorTest, initialState)
     EXPECT_EQ(nullptr, detector.detectorResolution());
 
     // region of interest
-    EXPECT_EQ(nullptr, detector.regionOfInterest());
+    EXPECT_FALSE(detector.hasExplicitRegionOfInterest());
 
     // behavior
     ASSERT_THROW(detector.axis(0), std::runtime_error);
@@ -92,23 +91,23 @@ TEST_F(SphericalDetectorTest, regionOfInterest)
     // creating region of interest
     double xlow(-2.0), ylow(1.0), xup(4.0), yup(3.0);
     detector.setRegionOfInterest(xlow, ylow, xup, yup);
-    EXPECT_FALSE(nullptr == detector.regionOfInterest());
-    EXPECT_EQ(detector.regionOfInterest()->getXlow(), xlow);
-    EXPECT_EQ(detector.regionOfInterest()->getYlow(), ylow);
-    EXPECT_EQ(detector.regionOfInterest()->getXup(), xup);
-    EXPECT_EQ(detector.regionOfInterest()->getYup(), yup);
+    EXPECT_TRUE(detector.hasExplicitRegionOfInterest());
+    EXPECT_EQ(detector.regionOfInterestBounds(0).first, xlow);
+    EXPECT_EQ(detector.regionOfInterestBounds(0).second, xup);
+    EXPECT_EQ(detector.regionOfInterestBounds(1).first, ylow);
+    EXPECT_EQ(detector.regionOfInterestBounds(1).second, yup);
 
     // replacing region of interest with a new one
     double xlow2(-2.1), ylow2(1.1), xup2(4.1), yup2(3.1);
     detector.setRegionOfInterest(xlow2, ylow2, xup2, yup2);
-    EXPECT_EQ(detector.regionOfInterest()->getXlow(), xlow2);
-    EXPECT_EQ(detector.regionOfInterest()->getYlow(), ylow2);
-    EXPECT_EQ(detector.regionOfInterest()->getXup(), xup2);
-    EXPECT_EQ(detector.regionOfInterest()->getYup(), yup2);
+    EXPECT_EQ(detector.regionOfInterestBounds(0).first, xlow2);
+    EXPECT_EQ(detector.regionOfInterestBounds(0).second, xup2);
+    EXPECT_EQ(detector.regionOfInterestBounds(1).first, ylow2);
+    EXPECT_EQ(detector.regionOfInterestBounds(1).second, yup2);
 
     // removing region of interest
     detector.resetRegionOfInterest();
-    EXPECT_TRUE(nullptr == detector.regionOfInterest());
+    EXPECT_FALSE(detector.hasExplicitRegionOfInterest());
 }
 
 // Create detector map in the presence of region of interest.
diff --git a/Tests/UnitTests/Core/Mask/RegionOfInterestTest.cpp b/Tests/UnitTests/Core/Mask/RegionOfInterestTest.cpp
index 00e6a6c89dec4146386f852811798eb3cdfca3ed..f8a0768ad24f498d5e60f48cb729f7e00b7fba80 100644
--- a/Tests/UnitTests/Core/Mask/RegionOfInterestTest.cpp
+++ b/Tests/UnitTests/Core/Mask/RegionOfInterestTest.cpp
@@ -1,4 +1,3 @@
-#include "Device/ProDetector/RegionOfInterest.h"
 #include "Device/Detector/SphericalDetector.h"
 #include "Tests/GTestWrapper/google_test.h"
 #include <memory>
@@ -16,29 +15,29 @@ TEST_F(RegionOfInterestTest, constructor)
 
     // creating region of interest
     double xlow(-1.9), ylow(1.1), xup(2.9), yup(2.85);
-    RegionOfInterest roi(detector.axes(), xlow, ylow, xup, yup);
-    EXPECT_EQ(roi.getXlow(), xlow);
-    EXPECT_EQ(roi.getYlow(), ylow);
-    EXPECT_EQ(roi.getXup(), xup);
-    EXPECT_EQ(roi.getYup(), yup);
+    detector.setRegionOfInterest(xlow, ylow, xup, yup);
+    EXPECT_EQ(detector.regionOfInterestBounds(0).first, xlow);
+    EXPECT_EQ(detector.regionOfInterestBounds(0).second, xup);
+    EXPECT_EQ(detector.regionOfInterestBounds(1).first, ylow);
+    EXPECT_EQ(detector.regionOfInterestBounds(1).second, yup);
 
     // checking total size of the detector and roi in it
-    EXPECT_EQ(roi.detectorSize(), 32u);
-    EXPECT_EQ(roi.roiSize(), 10u);
+    EXPECT_EQ(detector.totalSize(), 32u);
+    EXPECT_EQ(detector.sizeOfRegionOfInterest(), 10u);
 
     // converting global detector index to local roi index
-    EXPECT_EQ(roi.roiIndex(5), 0u);
-    EXPECT_EQ(roi.roiIndex(6), 1u);
-    EXPECT_EQ(roi.roiIndex(9), 2u);
-    EXPECT_EQ(roi.roiIndex(21), 8u);
-    EXPECT_EQ(roi.roiIndex(22), 9u);
-    EXPECT_THROW(roi.roiIndex(23), std::runtime_error);
-
-    // converting local ro index to global detector index
-    EXPECT_EQ(roi.detectorIndex(0), 5u);
-    EXPECT_EQ(roi.detectorIndex(1), 6u);
-    EXPECT_EQ(roi.detectorIndex(2), 9u);
-    EXPECT_EQ(roi.detectorIndex(9), 22u);
+    EXPECT_EQ(detector.detectorIndexToRegionOfInterestIndex(5), 0u);
+    EXPECT_EQ(detector.detectorIndexToRegionOfInterestIndex(6), 1u);
+    EXPECT_EQ(detector.detectorIndexToRegionOfInterestIndex(9), 2u);
+    EXPECT_EQ(detector.detectorIndexToRegionOfInterestIndex(21), 8u);
+    EXPECT_EQ(detector.detectorIndexToRegionOfInterestIndex(22), 9u);
+    EXPECT_THROW(detector.detectorIndexToRegionOfInterestIndex(23), std::runtime_error);
+
+    // converting local roi index to global detector index
+    EXPECT_EQ(detector.regionOfInterestIndexToDetectorIndex(0), 5u);
+    EXPECT_EQ(detector.regionOfInterestIndexToDetectorIndex(1), 6u);
+    EXPECT_EQ(detector.regionOfInterestIndexToDetectorIndex(2), 9u);
+    EXPECT_EQ(detector.regionOfInterestIndexToDetectorIndex(9), 22u);
 }
 
 //! Testing region of interest which is larger than the detector.
@@ -51,21 +50,21 @@ TEST_F(RegionOfInterestTest, largeArea)
 
     // creating region of interest
     double xlow(-3.9), ylow(-1.1), xup(6.9), yup(5.85);
-    RegionOfInterest roi(detector.axes(), xlow, ylow, xup, yup);
+    detector.setRegionOfInterest(xlow, ylow, xup, yup);
 
     // checking total size of the detector and roi in it
-    EXPECT_EQ(roi.detectorSize(), 32u);
-    EXPECT_EQ(roi.roiSize(), 32u);
+    EXPECT_EQ(detector.totalSize(), 32u);
+    EXPECT_EQ(detector.sizeOfRegionOfInterest(), 32u);
+
+    // converting local roi index to global detector index
+    EXPECT_EQ(detector.regionOfInterestIndexToDetectorIndex(5), 5u);
+    EXPECT_EQ(detector.regionOfInterestIndexToDetectorIndex(6), 6u);
+    EXPECT_EQ(detector.regionOfInterestIndexToDetectorIndex(9), 9u);
+    EXPECT_EQ(detector.regionOfInterestIndexToDetectorIndex(27), 27u);
 
     // converting global detector index to local roi index
-    EXPECT_EQ(roi.roiIndex(5), 5u);
-    EXPECT_EQ(roi.roiIndex(6), 6u);
-    EXPECT_EQ(roi.roiIndex(9), 9u);
-    EXPECT_EQ(roi.roiIndex(27), 27u);
-
-    // converting local ro index to global detector index
-    EXPECT_EQ(roi.detectorIndex(0), 0u);
-    EXPECT_EQ(roi.detectorIndex(27), 27u);
+    EXPECT_EQ(detector.detectorIndexToRegionOfInterestIndex(0), 0u);
+    EXPECT_EQ(detector.detectorIndexToRegionOfInterestIndex(27), 27u);
 }
 
 //! Testing clone
@@ -78,30 +77,30 @@ TEST_F(RegionOfInterestTest, clone)
 
     // creating region of interest
     double xlow(-1.9), ylow(1.1), xup(2.9), yup(2.85);
-    RegionOfInterest roi(detector.axes(), xlow, ylow, xup, yup);
+    detector.setRegionOfInterest(xlow, ylow, xup, yup);
 
-    std::unique_ptr<RegionOfInterest> clone(roi.clone());
+    std::unique_ptr<SphericalDetector> clone(detector.clone());
 
-    EXPECT_EQ(clone->getXlow(), xlow);
-    EXPECT_EQ(clone->getYlow(), ylow);
-    EXPECT_EQ(clone->getXup(), xup);
-    EXPECT_EQ(clone->getYup(), yup);
+    EXPECT_EQ(clone->regionOfInterestBounds(0).first, xlow);
+    EXPECT_EQ(clone->regionOfInterestBounds(0).second, xup);
+    EXPECT_EQ(clone->regionOfInterestBounds(1).first, ylow);
+    EXPECT_EQ(clone->regionOfInterestBounds(1).second, yup);
 
     // checking total size of the detector and roi in it
-    EXPECT_EQ(clone->detectorSize(), 32u);
-    EXPECT_EQ(clone->roiSize(), 10u);
+    EXPECT_EQ(clone->totalSize(), 32u);
+    EXPECT_EQ(clone->sizeOfRegionOfInterest(), 10u);
 
     // converting global detector index to local roi index
-    EXPECT_EQ(clone->roiIndex(5), 0u);
-    EXPECT_EQ(clone->roiIndex(6), 1u);
-    EXPECT_EQ(clone->roiIndex(9), 2u);
-    EXPECT_EQ(clone->roiIndex(21), 8u);
-    EXPECT_EQ(clone->roiIndex(22), 9u);
-    EXPECT_THROW(clone->roiIndex(23), std::runtime_error);
-
-    // converting local ro index to global detector index
-    EXPECT_EQ(clone->detectorIndex(0), 5u);
-    EXPECT_EQ(clone->detectorIndex(1), 6u);
-    EXPECT_EQ(clone->detectorIndex(2), 9u);
-    EXPECT_EQ(clone->detectorIndex(9), 22u);
+    EXPECT_EQ(clone->detectorIndexToRegionOfInterestIndex(5), 0u);
+    EXPECT_EQ(clone->detectorIndexToRegionOfInterestIndex(6), 1u);
+    EXPECT_EQ(clone->detectorIndexToRegionOfInterestIndex(9), 2u);
+    EXPECT_EQ(clone->detectorIndexToRegionOfInterestIndex(21), 8u);
+    EXPECT_EQ(clone->detectorIndexToRegionOfInterestIndex(22), 9u);
+    EXPECT_THROW(clone->detectorIndexToRegionOfInterestIndex(23), std::runtime_error);
+
+    // converting local roi index to global detector index
+    EXPECT_EQ(clone->regionOfInterestIndexToDetectorIndex(0), 5u);
+    EXPECT_EQ(clone->regionOfInterestIndexToDetectorIndex(1), 6u);
+    EXPECT_EQ(clone->regionOfInterestIndexToDetectorIndex(2), 9u);
+    EXPECT_EQ(clone->regionOfInterestIndexToDetectorIndex(9), 22u);
 }
diff --git a/auto/Wrap/libBornAgainDevice.py b/auto/Wrap/libBornAgainDevice.py
index 31cff248f12d06ce35071b55d50cfad6583da4c8..424f019a2f55e014359d858c2aed27cbace1bc0a 100644
--- a/auto/Wrap/libBornAgainDevice.py
+++ b/auto/Wrap/libBornAgainDevice.py
@@ -4040,10 +4040,22 @@ class IDetector(libBornAgainBase.ICloneable, libBornAgainParam.INode):
         r"""sizeOfRegionOfInterest(IDetector self) -> size_t"""
         return _libBornAgainDevice.IDetector_sizeOfRegionOfInterest(self)
 
+    def hasExplicitRegionOfInterest(self):
+        r"""hasExplicitRegionOfInterest(IDetector self) -> bool"""
+        return _libBornAgainDevice.IDetector_hasExplicitRegionOfInterest(self)
+
+    def axesClippedToRegionOfInterest(self):
+        r"""axesClippedToRegionOfInterest(IDetector self) -> CloneableVector< IAxis >"""
+        return _libBornAgainDevice.IDetector_axesClippedToRegionOfInterest(self)
+
     def regionOfInterestIndexToDetectorIndex(self, regionOfInterestIndex):
         r"""regionOfInterestIndexToDetectorIndex(IDetector self, size_t const regionOfInterestIndex) -> size_t"""
         return _libBornAgainDevice.IDetector_regionOfInterestIndexToDetectorIndex(self, regionOfInterestIndex)
 
+    def detectorIndexToRegionOfInterestIndex(self, detectorIndex):
+        r"""detectorIndexToRegionOfInterestIndex(IDetector self, size_t const detectorIndex) -> size_t"""
+        return _libBornAgainDevice.IDetector_detectorIndexToRegionOfInterestIndex(self, detectorIndex)
+
     def applyDetectorResolution(self, p_intensity_map):
         r"""
         applyDetectorResolution(IDetector self, IntensityData p_intensity_map)
@@ -4104,15 +4116,9 @@ class IDetector(libBornAgainBase.ICloneable, libBornAgainParam.INode):
         """
         return _libBornAgainDevice.IDetector_numberOfSimulationElements(self)
 
-    def regionOfInterest(self):
-        r"""
-        regionOfInterest(IDetector self) -> RegionOfInterest const *
-        virtual const RegionOfInterest* IDetector::regionOfInterest() const =0
-
-        Returns region of interest if exists. 
-
-        """
-        return _libBornAgainDevice.IDetector_regionOfInterest(self)
+    def regionOfInterestBounds(self, iAxis):
+        r"""regionOfInterestBounds(IDetector self, size_t iAxis) -> pvacuum_double_t"""
+        return _libBornAgainDevice.IDetector_regionOfInterestBounds(self, iAxis)
 
 # Register IDetector in _libBornAgainDevice:
 _libBornAgainDevice.IDetector_swigregister(IDetector)
@@ -4191,16 +4197,6 @@ class IDetector2D(IDetector):
         """
         return _libBornAgainDevice.IDetector2D_maskAll(self)
 
-    def regionOfInterest(self):
-        r"""
-        regionOfInterest(IDetector2D self) -> RegionOfInterest const *
-        const RegionOfInterest * IDetector2D::regionOfInterest() const override
-
-        Returns region of interest if exists. 
-
-        """
-        return _libBornAgainDevice.IDetector2D_regionOfInterest(self)
-
     def setRegionOfInterest(self, xlow, ylow, xup, yup):
         r"""
         setRegionOfInterest(IDetector2D self, double xlow, double ylow, double xup, double yup)
@@ -4211,15 +4207,13 @@ class IDetector2D(IDetector):
         """
         return _libBornAgainDevice.IDetector2D_setRegionOfInterest(self, xlow, ylow, xup, yup)
 
-    def resetRegionOfInterest(self):
-        r"""
-        resetRegionOfInterest(IDetector2D self)
-        void IDetector2D::resetRegionOfInterest() override
-
-        Resets region of interest making whole detector plane available for the simulation. 
+    def regionOfInterestIndexToDetectorIndex(self, regionOfInterestIndex):
+        r"""regionOfInterestIndexToDetectorIndex(IDetector2D self, size_t const regionOfInterestIndex) -> size_t"""
+        return _libBornAgainDevice.IDetector2D_regionOfInterestIndexToDetectorIndex(self, regionOfInterestIndex)
 
-        """
-        return _libBornAgainDevice.IDetector2D_resetRegionOfInterest(self)
+    def detectorIndexToRegionOfInterestIndex(self, detectorIndex):
+        r"""detectorIndexToRegionOfInterestIndex(IDetector2D self, size_t const detectorIndex) -> size_t"""
+        return _libBornAgainDevice.IDetector2D_detectorIndexToRegionOfInterestIndex(self, detectorIndex)
 
     def active_indices(self):
         r"""
diff --git a/auto/Wrap/libBornAgainDevice_wrap.cpp b/auto/Wrap/libBornAgainDevice_wrap.cpp
index 91b85a4e1ee4baada1a66a13c34b399861b69528..4fd5ea54c921ba6affd823f05818c3d07b89a6f9 100644
--- a/auto/Wrap/libBornAgainDevice_wrap.cpp
+++ b/auto/Wrap/libBornAgainDevice_wrap.cpp
@@ -3145,72 +3145,71 @@ namespace Swig {
 #define SWIGTYPE_p_Rectangle swig_types[45]
 #define SWIGTYPE_p_RectangularDetector swig_types[46]
 #define SWIGTYPE_p_RectangularPixel swig_types[47]
-#define SWIGTYPE_p_RegionOfInterest swig_types[48]
-#define SWIGTYPE_p_ResolutionFunction2DGaussian swig_types[49]
-#define SWIGTYPE_p_ScanResolution swig_types[50]
-#define SWIGTYPE_p_SimulationResult swig_types[51]
-#define SWIGTYPE_p_SphericalDetector swig_types[52]
-#define SWIGTYPE_p_VerticalLine swig_types[53]
-#define SWIGTYPE_p_allocator_type swig_types[54]
-#define SWIGTYPE_p_bool swig_types[55]
-#define SWIGTYPE_p_char swig_types[56]
-#define SWIGTYPE_p_const_iterator swig_types[57]
-#define SWIGTYPE_p_corr_matrix_t swig_types[58]
-#define SWIGTYPE_p_difference_type swig_types[59]
-#define SWIGTYPE_p_double swig_types[60]
-#define SWIGTYPE_p_first_type swig_types[61]
-#define SWIGTYPE_p_int swig_types[62]
-#define SWIGTYPE_p_iterator swig_types[63]
-#define SWIGTYPE_p_key_type swig_types[64]
-#define SWIGTYPE_p_long_long swig_types[65]
-#define SWIGTYPE_p_mapped_type swig_types[66]
-#define SWIGTYPE_p_p_ICoordSystem swig_types[67]
-#define SWIGTYPE_p_p_PyObject swig_types[68]
-#define SWIGTYPE_p_parameters_t swig_types[69]
-#define SWIGTYPE_p_second_type swig_types[70]
-#define SWIGTYPE_p_short swig_types[71]
-#define SWIGTYPE_p_signed_char swig_types[72]
-#define SWIGTYPE_p_size_type swig_types[73]
-#define SWIGTYPE_p_std__allocatorT_BasicVector3DT_double_t_t swig_types[74]
-#define SWIGTYPE_p_std__allocatorT_BasicVector3DT_std__complexT_double_t_t_t swig_types[75]
-#define SWIGTYPE_p_std__allocatorT_double_t swig_types[76]
-#define SWIGTYPE_p_std__allocatorT_int_t swig_types[77]
-#define SWIGTYPE_p_std__allocatorT_std__complexT_double_t_t swig_types[78]
-#define SWIGTYPE_p_std__allocatorT_std__pairT_double_double_t_t swig_types[79]
-#define SWIGTYPE_p_std__allocatorT_std__pairT_std__string_const_double_t_t swig_types[80]
-#define SWIGTYPE_p_std__allocatorT_std__string_t swig_types[81]
-#define SWIGTYPE_p_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t swig_types[82]
-#define SWIGTYPE_p_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t swig_types[83]
-#define SWIGTYPE_p_std__allocatorT_unsigned_long_t swig_types[84]
-#define SWIGTYPE_p_std__complexT_double_t swig_types[85]
-#define SWIGTYPE_p_std__functionT_void_fSimulationAreaIterator_const_RF_t swig_types[86]
-#define SWIGTYPE_p_std__invalid_argument swig_types[87]
-#define SWIGTYPE_p_std__lessT_std__string_t swig_types[88]
-#define SWIGTYPE_p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t swig_types[89]
-#define SWIGTYPE_p_std__pairT_double_double_t swig_types[90]
-#define SWIGTYPE_p_std__vectorT_AxisInfo_std__allocatorT_AxisInfo_t_t swig_types[91]
-#define SWIGTYPE_p_std__vectorT_BasicVector3DT_double_t_std__allocatorT_BasicVector3DT_double_t_t_t swig_types[92]
-#define SWIGTYPE_p_std__vectorT_BasicVector3DT_std__complexT_double_t_t_std__allocatorT_BasicVector3DT_std__complexT_double_t_t_t_t swig_types[93]
-#define SWIGTYPE_p_std__vectorT_INode_const_p_std__allocatorT_INode_const_p_t_t swig_types[94]
-#define SWIGTYPE_p_std__vectorT_SimulationElement_std__allocatorT_SimulationElement_t_t swig_types[95]
-#define SWIGTYPE_p_std__vectorT_double_std__allocatorT_double_t_t swig_types[96]
-#define SWIGTYPE_p_std__vectorT_int_std__allocatorT_int_t_t swig_types[97]
-#define SWIGTYPE_p_std__vectorT_size_t_std__allocatorT_size_t_t_t swig_types[98]
-#define SWIGTYPE_p_std__vectorT_std__complexT_double_t_std__allocatorT_std__complexT_double_t_t_t swig_types[99]
-#define SWIGTYPE_p_std__vectorT_std__pairT_double_double_t_std__allocatorT_std__pairT_double_double_t_t_t swig_types[100]
-#define SWIGTYPE_p_std__vectorT_std__string_std__allocatorT_std__string_t_t swig_types[101]
-#define SWIGTYPE_p_std__vectorT_std__vectorT_double_std__allocatorT_double_t_t_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t_t swig_types[102]
-#define SWIGTYPE_p_std__vectorT_std__vectorT_int_std__allocatorT_int_t_t_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t_t swig_types[103]
-#define SWIGTYPE_p_std__vectorT_unsigned_int_std__allocatorT_unsigned_int_t_t swig_types[104]
-#define SWIGTYPE_p_std__vectorT_unsigned_long_std__allocatorT_unsigned_long_t_t swig_types[105]
-#define SWIGTYPE_p_swig__SwigPyIterator swig_types[106]
-#define SWIGTYPE_p_unsigned_char swig_types[107]
-#define SWIGTYPE_p_unsigned_int swig_types[108]
-#define SWIGTYPE_p_unsigned_long_long swig_types[109]
-#define SWIGTYPE_p_unsigned_short swig_types[110]
-#define SWIGTYPE_p_value_type swig_types[111]
-static swig_type_info *swig_types[113];
-static swig_module_info swig_module = {swig_types, 112, 0, 0, 0, 0};
+#define SWIGTYPE_p_ResolutionFunction2DGaussian swig_types[48]
+#define SWIGTYPE_p_ScanResolution swig_types[49]
+#define SWIGTYPE_p_SimulationResult swig_types[50]
+#define SWIGTYPE_p_SphericalDetector swig_types[51]
+#define SWIGTYPE_p_VerticalLine swig_types[52]
+#define SWIGTYPE_p_allocator_type swig_types[53]
+#define SWIGTYPE_p_bool swig_types[54]
+#define SWIGTYPE_p_char swig_types[55]
+#define SWIGTYPE_p_const_iterator swig_types[56]
+#define SWIGTYPE_p_corr_matrix_t swig_types[57]
+#define SWIGTYPE_p_difference_type swig_types[58]
+#define SWIGTYPE_p_double swig_types[59]
+#define SWIGTYPE_p_first_type swig_types[60]
+#define SWIGTYPE_p_int swig_types[61]
+#define SWIGTYPE_p_iterator swig_types[62]
+#define SWIGTYPE_p_key_type swig_types[63]
+#define SWIGTYPE_p_long_long swig_types[64]
+#define SWIGTYPE_p_mapped_type swig_types[65]
+#define SWIGTYPE_p_p_ICoordSystem swig_types[66]
+#define SWIGTYPE_p_p_PyObject swig_types[67]
+#define SWIGTYPE_p_parameters_t swig_types[68]
+#define SWIGTYPE_p_second_type swig_types[69]
+#define SWIGTYPE_p_short swig_types[70]
+#define SWIGTYPE_p_signed_char swig_types[71]
+#define SWIGTYPE_p_size_type swig_types[72]
+#define SWIGTYPE_p_std__allocatorT_BasicVector3DT_double_t_t swig_types[73]
+#define SWIGTYPE_p_std__allocatorT_BasicVector3DT_std__complexT_double_t_t_t swig_types[74]
+#define SWIGTYPE_p_std__allocatorT_double_t swig_types[75]
+#define SWIGTYPE_p_std__allocatorT_int_t swig_types[76]
+#define SWIGTYPE_p_std__allocatorT_std__complexT_double_t_t swig_types[77]
+#define SWIGTYPE_p_std__allocatorT_std__pairT_double_double_t_t swig_types[78]
+#define SWIGTYPE_p_std__allocatorT_std__pairT_std__string_const_double_t_t swig_types[79]
+#define SWIGTYPE_p_std__allocatorT_std__string_t swig_types[80]
+#define SWIGTYPE_p_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t swig_types[81]
+#define SWIGTYPE_p_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t swig_types[82]
+#define SWIGTYPE_p_std__allocatorT_unsigned_long_t swig_types[83]
+#define SWIGTYPE_p_std__complexT_double_t swig_types[84]
+#define SWIGTYPE_p_std__functionT_void_fSimulationAreaIterator_const_RF_t swig_types[85]
+#define SWIGTYPE_p_std__invalid_argument swig_types[86]
+#define SWIGTYPE_p_std__lessT_std__string_t swig_types[87]
+#define SWIGTYPE_p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t swig_types[88]
+#define SWIGTYPE_p_std__pairT_double_double_t swig_types[89]
+#define SWIGTYPE_p_std__vectorT_AxisInfo_std__allocatorT_AxisInfo_t_t swig_types[90]
+#define SWIGTYPE_p_std__vectorT_BasicVector3DT_double_t_std__allocatorT_BasicVector3DT_double_t_t_t swig_types[91]
+#define SWIGTYPE_p_std__vectorT_BasicVector3DT_std__complexT_double_t_t_std__allocatorT_BasicVector3DT_std__complexT_double_t_t_t_t swig_types[92]
+#define SWIGTYPE_p_std__vectorT_INode_const_p_std__allocatorT_INode_const_p_t_t swig_types[93]
+#define SWIGTYPE_p_std__vectorT_SimulationElement_std__allocatorT_SimulationElement_t_t swig_types[94]
+#define SWIGTYPE_p_std__vectorT_double_std__allocatorT_double_t_t swig_types[95]
+#define SWIGTYPE_p_std__vectorT_int_std__allocatorT_int_t_t swig_types[96]
+#define SWIGTYPE_p_std__vectorT_size_t_std__allocatorT_size_t_t_t swig_types[97]
+#define SWIGTYPE_p_std__vectorT_std__complexT_double_t_std__allocatorT_std__complexT_double_t_t_t swig_types[98]
+#define SWIGTYPE_p_std__vectorT_std__pairT_double_double_t_std__allocatorT_std__pairT_double_double_t_t_t swig_types[99]
+#define SWIGTYPE_p_std__vectorT_std__string_std__allocatorT_std__string_t_t swig_types[100]
+#define SWIGTYPE_p_std__vectorT_std__vectorT_double_std__allocatorT_double_t_t_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t_t swig_types[101]
+#define SWIGTYPE_p_std__vectorT_std__vectorT_int_std__allocatorT_int_t_t_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t_t swig_types[102]
+#define SWIGTYPE_p_std__vectorT_unsigned_int_std__allocatorT_unsigned_int_t_t swig_types[103]
+#define SWIGTYPE_p_std__vectorT_unsigned_long_std__allocatorT_unsigned_long_t_t swig_types[104]
+#define SWIGTYPE_p_swig__SwigPyIterator swig_types[105]
+#define SWIGTYPE_p_unsigned_char swig_types[106]
+#define SWIGTYPE_p_unsigned_int swig_types[107]
+#define SWIGTYPE_p_unsigned_long_long swig_types[108]
+#define SWIGTYPE_p_unsigned_short swig_types[109]
+#define SWIGTYPE_p_value_type swig_types[110]
+static swig_type_info *swig_types[112];
+static swig_module_info swig_module = {swig_types, 111, 0, 0, 0, 0};
 #define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name)
 #define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name)
 
@@ -36147,6 +36146,52 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_IDetector_hasExplicitRegionOfInterest(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  IDetector *arg1 = (IDetector *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject *swig_obj[1] ;
+  bool result;
+  
+  if (!args) SWIG_fail;
+  swig_obj[0] = args;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IDetector, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IDetector_hasExplicitRegionOfInterest" "', argument " "1"" of type '" "IDetector const *""'"); 
+  }
+  arg1 = reinterpret_cast< IDetector * >(argp1);
+  result = (bool)((IDetector const *)arg1)->hasExplicitRegionOfInterest();
+  resultobj = SWIG_From_bool(static_cast< bool >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IDetector_axesClippedToRegionOfInterest(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  IDetector *arg1 = (IDetector *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject *swig_obj[1] ;
+  SwigValueWrapper< CloneableVector< IAxis > > result;
+  
+  if (!args) SWIG_fail;
+  swig_obj[0] = args;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IDetector, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IDetector_axesClippedToRegionOfInterest" "', argument " "1"" of type '" "IDetector const *""'"); 
+  }
+  arg1 = reinterpret_cast< IDetector * >(argp1);
+  result = ((IDetector const *)arg1)->axesClippedToRegionOfInterest();
+  resultobj = SWIG_NewPointerObj((new CloneableVector< IAxis >(static_cast< const CloneableVector< IAxis >& >(result))), SWIGTYPE_p_CloneableVectorT_IAxis_t, SWIG_POINTER_OWN |  0 );
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_IDetector_regionOfInterestIndexToDetectorIndex(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   IDetector *arg1 = (IDetector *) 0 ;
@@ -36177,6 +36222,36 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_IDetector_detectorIndexToRegionOfInterestIndex(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  IDetector *arg1 = (IDetector *) 0 ;
+  size_t arg2 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  size_t val2 ;
+  int ecode2 = 0 ;
+  PyObject *swig_obj[2] ;
+  size_t result;
+  
+  if (!SWIG_Python_UnpackTuple(args, "IDetector_detectorIndexToRegionOfInterestIndex", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IDetector, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IDetector_detectorIndexToRegionOfInterestIndex" "', argument " "1"" of type '" "IDetector const *""'"); 
+  }
+  arg1 = reinterpret_cast< IDetector * >(argp1);
+  ecode2 = SWIG_AsVal_size_t(swig_obj[1], &val2);
+  if (!SWIG_IsOK(ecode2)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "IDetector_detectorIndexToRegionOfInterestIndex" "', argument " "2"" of type '" "size_t""'");
+  } 
+  arg2 = static_cast< size_t >(val2);
+  result = ((IDetector const *)arg1)->detectorIndexToRegionOfInterestIndex(arg2);
+  resultobj = SWIG_From_size_t(static_cast< size_t >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_IDetector_applyDetectorResolution(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   IDetector *arg1 = (IDetector *) 0 ;
@@ -36331,23 +36406,30 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_IDetector_regionOfInterest(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+SWIGINTERN PyObject *_wrap_IDetector_regionOfInterestBounds(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   IDetector *arg1 = (IDetector *) 0 ;
+  size_t arg2 ;
   void *argp1 = 0 ;
   int res1 = 0 ;
-  PyObject *swig_obj[1] ;
-  RegionOfInterest *result = 0 ;
+  size_t val2 ;
+  int ecode2 = 0 ;
+  PyObject *swig_obj[2] ;
+  std::pair< double,double > result;
   
-  if (!args) SWIG_fail;
-  swig_obj[0] = args;
+  if (!SWIG_Python_UnpackTuple(args, "IDetector_regionOfInterestBounds", 2, 2, swig_obj)) SWIG_fail;
   res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IDetector, 0 |  0 );
   if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IDetector_regionOfInterest" "', argument " "1"" of type '" "IDetector const *""'"); 
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IDetector_regionOfInterestBounds" "', argument " "1"" of type '" "IDetector const *""'"); 
   }
   arg1 = reinterpret_cast< IDetector * >(argp1);
-  result = (RegionOfInterest *)((IDetector const *)arg1)->regionOfInterest();
-  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_RegionOfInterest, 0 |  0 );
+  ecode2 = SWIG_AsVal_size_t(swig_obj[1], &val2);
+  if (!SWIG_IsOK(ecode2)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "IDetector_regionOfInterestBounds" "', argument " "2"" of type '" "size_t""'");
+  } 
+  arg2 = static_cast< size_t >(val2);
+  result = ((IDetector const *)arg1)->regionOfInterestBounds(arg2);
+  resultobj = swig::from(static_cast< std::pair< double,double > >(result));
   return resultobj;
 fail:
   return NULL;
@@ -36640,29 +36722,6 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_IDetector2D_regionOfInterest(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
-  PyObject *resultobj = 0;
-  IDetector2D *arg1 = (IDetector2D *) 0 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  PyObject *swig_obj[1] ;
-  RegionOfInterest *result = 0 ;
-  
-  if (!args) SWIG_fail;
-  swig_obj[0] = args;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IDetector2D, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IDetector2D_regionOfInterest" "', argument " "1"" of type '" "IDetector2D const *""'"); 
-  }
-  arg1 = reinterpret_cast< IDetector2D * >(argp1);
-  result = (RegionOfInterest *)((IDetector2D const *)arg1)->regionOfInterest();
-  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_RegionOfInterest, 0 |  0 );
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
 SWIGINTERN PyObject *_wrap_IDetector2D_setRegionOfInterest(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   IDetector2D *arg1 = (IDetector2D *) 0 ;
@@ -36716,22 +36775,60 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_IDetector2D_resetRegionOfInterest(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+SWIGINTERN PyObject *_wrap_IDetector2D_regionOfInterestIndexToDetectorIndex(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *resultobj = 0;
   IDetector2D *arg1 = (IDetector2D *) 0 ;
+  size_t arg2 ;
   void *argp1 = 0 ;
   int res1 = 0 ;
-  PyObject *swig_obj[1] ;
+  size_t val2 ;
+  int ecode2 = 0 ;
+  PyObject *swig_obj[2] ;
+  size_t result;
   
-  if (!args) SWIG_fail;
-  swig_obj[0] = args;
+  if (!SWIG_Python_UnpackTuple(args, "IDetector2D_regionOfInterestIndexToDetectorIndex", 2, 2, swig_obj)) SWIG_fail;
   res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IDetector2D, 0 |  0 );
   if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IDetector2D_resetRegionOfInterest" "', argument " "1"" of type '" "IDetector2D *""'"); 
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IDetector2D_regionOfInterestIndexToDetectorIndex" "', argument " "1"" of type '" "IDetector2D const *""'"); 
   }
   arg1 = reinterpret_cast< IDetector2D * >(argp1);
-  (arg1)->resetRegionOfInterest();
-  resultobj = SWIG_Py_Void();
+  ecode2 = SWIG_AsVal_size_t(swig_obj[1], &val2);
+  if (!SWIG_IsOK(ecode2)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "IDetector2D_regionOfInterestIndexToDetectorIndex" "', argument " "2"" of type '" "size_t""'");
+  } 
+  arg2 = static_cast< size_t >(val2);
+  result = ((IDetector2D const *)arg1)->regionOfInterestIndexToDetectorIndex(arg2);
+  resultobj = SWIG_From_size_t(static_cast< size_t >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IDetector2D_detectorIndexToRegionOfInterestIndex(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  IDetector2D *arg1 = (IDetector2D *) 0 ;
+  size_t arg2 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  size_t val2 ;
+  int ecode2 = 0 ;
+  PyObject *swig_obj[2] ;
+  size_t result;
+  
+  if (!SWIG_Python_UnpackTuple(args, "IDetector2D_detectorIndexToRegionOfInterestIndex", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IDetector2D, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IDetector2D_detectorIndexToRegionOfInterestIndex" "', argument " "1"" of type '" "IDetector2D const *""'"); 
+  }
+  arg1 = reinterpret_cast< IDetector2D * >(argp1);
+  ecode2 = SWIG_AsVal_size_t(swig_obj[1], &val2);
+  if (!SWIG_IsOK(ecode2)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "IDetector2D_detectorIndexToRegionOfInterestIndex" "', argument " "2"" of type '" "size_t""'");
+  } 
+  arg2 = static_cast< size_t >(val2);
+  result = ((IDetector2D const *)arg1)->detectorIndexToRegionOfInterestIndex(arg2);
+  resultobj = SWIG_From_size_t(static_cast< size_t >(result));
   return resultobj;
 fail:
   return NULL;
@@ -45589,7 +45686,10 @@ static PyMethodDef SwigMethods[] = {
 		"\n"
 		""},
 	 { "IDetector_sizeOfRegionOfInterest", _wrap_IDetector_sizeOfRegionOfInterest, METH_O, "IDetector_sizeOfRegionOfInterest(IDetector self) -> size_t"},
+	 { "IDetector_hasExplicitRegionOfInterest", _wrap_IDetector_hasExplicitRegionOfInterest, METH_O, "IDetector_hasExplicitRegionOfInterest(IDetector self) -> bool"},
+	 { "IDetector_axesClippedToRegionOfInterest", _wrap_IDetector_axesClippedToRegionOfInterest, METH_O, "IDetector_axesClippedToRegionOfInterest(IDetector self) -> CloneableVector< IAxis >"},
 	 { "IDetector_regionOfInterestIndexToDetectorIndex", _wrap_IDetector_regionOfInterestIndexToDetectorIndex, METH_VARARGS, "IDetector_regionOfInterestIndexToDetectorIndex(IDetector self, size_t const regionOfInterestIndex) -> size_t"},
+	 { "IDetector_detectorIndexToRegionOfInterestIndex", _wrap_IDetector_detectorIndexToRegionOfInterestIndex, METH_VARARGS, "IDetector_detectorIndexToRegionOfInterestIndex(IDetector self, size_t const detectorIndex) -> size_t"},
 	 { "IDetector_applyDetectorResolution", _wrap_IDetector_applyDetectorResolution, METH_VARARGS, "\n"
 		"IDetector_applyDetectorResolution(IDetector self, IntensityData p_intensity_map)\n"
 		"void IDetector::applyDetectorResolution(OutputData< double > *p_intensity_map) const\n"
@@ -45632,13 +45732,7 @@ static PyMethodDef SwigMethods[] = {
 		"Returns number of simulation elements. \n"
 		"\n"
 		""},
-	 { "IDetector_regionOfInterest", _wrap_IDetector_regionOfInterest, METH_O, "\n"
-		"IDetector_regionOfInterest(IDetector self) -> RegionOfInterest const *\n"
-		"virtual const RegionOfInterest* IDetector::regionOfInterest() const =0\n"
-		"\n"
-		"Returns region of interest if exists. \n"
-		"\n"
-		""},
+	 { "IDetector_regionOfInterestBounds", _wrap_IDetector_regionOfInterestBounds, METH_VARARGS, "IDetector_regionOfInterestBounds(IDetector self, size_t iAxis) -> pvacuum_double_t"},
 	 { "IDetector_swigregister", IDetector_swigregister, METH_O, NULL},
 	 { "IDetector2D_clone", _wrap_IDetector2D_clone, METH_O, "\n"
 		"IDetector2D_clone(IDetector2D self) -> IDetector2D\n"
@@ -45687,13 +45781,6 @@ static PyMethodDef SwigMethods[] = {
 		"Put the mask for all detector channels (i.e. exclude whole detector from the analysis) \n"
 		"\n"
 		""},
-	 { "IDetector2D_regionOfInterest", _wrap_IDetector2D_regionOfInterest, METH_O, "\n"
-		"IDetector2D_regionOfInterest(IDetector2D self) -> RegionOfInterest const *\n"
-		"const RegionOfInterest * IDetector2D::regionOfInterest() const override\n"
-		"\n"
-		"Returns region of interest if exists. \n"
-		"\n"
-		""},
 	 { "IDetector2D_setRegionOfInterest", _wrap_IDetector2D_setRegionOfInterest, METH_VARARGS, "\n"
 		"IDetector2D_setRegionOfInterest(IDetector2D self, double xlow, double ylow, double xup, double yup)\n"
 		"void IDetector2D::setRegionOfInterest(double xlow, double ylow, double xup, double yup)\n"
@@ -45701,13 +45788,8 @@ static PyMethodDef SwigMethods[] = {
 		"Sets rectangular region of interest with lower left and upper right corners defined. \n"
 		"\n"
 		""},
-	 { "IDetector2D_resetRegionOfInterest", _wrap_IDetector2D_resetRegionOfInterest, METH_O, "\n"
-		"IDetector2D_resetRegionOfInterest(IDetector2D self)\n"
-		"void IDetector2D::resetRegionOfInterest() override\n"
-		"\n"
-		"Resets region of interest making whole detector plane available for the simulation. \n"
-		"\n"
-		""},
+	 { "IDetector2D_regionOfInterestIndexToDetectorIndex", _wrap_IDetector2D_regionOfInterestIndexToDetectorIndex, METH_VARARGS, "IDetector2D_regionOfInterestIndexToDetectorIndex(IDetector2D self, size_t const regionOfInterestIndex) -> size_t"},
+	 { "IDetector2D_detectorIndexToRegionOfInterestIndex", _wrap_IDetector2D_detectorIndexToRegionOfInterestIndex, METH_VARARGS, "IDetector2D_detectorIndexToRegionOfInterestIndex(IDetector2D self, size_t const detectorIndex) -> size_t"},
 	 { "IDetector2D_active_indices", _wrap_IDetector2D_active_indices, METH_O, "\n"
 		"IDetector2D_active_indices(IDetector2D self) -> std::vector< size_t,std::allocator< size_t > >\n"
 		"std::vector< size_t > IDetector2D::active_indices() const\n"
@@ -46791,7 +46873,6 @@ static swig_type_info _swigt__p_RealLimits = {"_p_RealLimits", "RealLimits *", 0
 static swig_type_info _swigt__p_Rectangle = {"_p_Rectangle", "Rectangle *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_RectangularDetector = {"_p_RectangularDetector", "RectangularDetector *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_RectangularPixel = {"_p_RectangularPixel", "RectangularPixel *", 0, 0, (void*)0, 0};
-static swig_type_info _swigt__p_RegionOfInterest = {"_p_RegionOfInterest", "RegionOfInterest *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_ResolutionFunction2DGaussian = {"_p_ResolutionFunction2DGaussian", "ResolutionFunction2DGaussian *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_ScanResolution = {"_p_ScanResolution", "ScanResolution *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_SimulationResult = {"_p_SimulationResult", "SimulationResult *", 0, 0, (void*)0, 0};
@@ -46905,7 +46986,6 @@ static swig_type_info *swig_type_initial[] = {
   &_swigt__p_Rectangle,
   &_swigt__p_RectangularDetector,
   &_swigt__p_RectangularPixel,
-  &_swigt__p_RegionOfInterest,
   &_swigt__p_ResolutionFunction2DGaussian,
   &_swigt__p_ScanResolution,
   &_swigt__p_SimulationResult,
@@ -47019,7 +47099,6 @@ static swig_cast_info _swigc__p_RealLimits[] = {  {&_swigt__p_RealLimits, 0, 0,
 static swig_cast_info _swigc__p_Rectangle[] = {  {&_swigt__p_Rectangle, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_RectangularDetector[] = {  {&_swigt__p_RectangularDetector, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_RectangularPixel[] = {  {&_swigt__p_RectangularPixel, 0, 0, 0},{0, 0, 0, 0}};
-static swig_cast_info _swigc__p_RegionOfInterest[] = {  {&_swigt__p_RegionOfInterest, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_ResolutionFunction2DGaussian[] = {  {&_swigt__p_ResolutionFunction2DGaussian, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_ScanResolution[] = {  {&_swigt__p_ScanResolution, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_SimulationResult[] = {  {&_swigt__p_SimulationResult, 0, 0, 0},{0, 0, 0, 0}};
@@ -47133,7 +47212,6 @@ static swig_cast_info *swig_cast_initial[] = {
   _swigc__p_Rectangle,
   _swigc__p_RectangularDetector,
   _swigc__p_RectangularPixel,
-  _swigc__p_RegionOfInterest,
   _swigc__p_ResolutionFunction2DGaussian,
   _swigc__p_ScanResolution,
   _swigc__p_SimulationResult,