Repository: silx-kit/silx
Issue: [ROI] Bug calculating ROIs on curves with negative or unsorted X values

The calculation is made on the unsorted values what gives wrong results.

Changed files:
silx/gui/plot/CurvesROIWidget.py, silx/gui/plot/test/testCurvesROIWidget.py

diff --git a/silx/gui/plot/CurvesROIWidget.py b/silx/gui/plot/CurvesROIWidget.py
index 9cc06d602..13c3de085 100644
--- a/silx/gui/plot/CurvesROIWidget.py
+++ b/silx/gui/plot/CurvesROIWidget.py
@@ -636,6 +636,12 @@ class CurvesROIDockWidget(qt.QDockWidget):
         self.roiWidget = CurvesROIWidget(self, name)
         """Main widget of type :class:`CurvesROIWidget`"""
 
+        # convenience methods to offer a simpler API allowing to ignore
+        # the details of the underlying implementation
+        self.calculateROIs = self.calculateRois
+        self.setRois = self.roiWidget.setRois
+        self.getRois = self.roiWidget.getRois
+
         self.layout().setContentsMargins(0, 0, 0, 0)
         self.setWidget(self.roiWidget)
 
@@ -860,7 +866,7 @@ class CurvesROIDockWidget(qt.QDockWidget):
         """Recompute ROIs when active curve changed."""
         self.calculateROIs()
 
-    def calculateROIs(self, roiList=None, roiDict=None):
+    def calculateRois(self, roiList=None, roiDict=None):
         """Compute ROI information"""
         if roiList is None or roiDict is None:
             roiList, roiDict = self.roiWidget.getROIListAndDict()
@@ -868,6 +874,7 @@ class CurvesROIDockWidget(qt.QDockWidget):
         activeCurve = self.plot.getActiveCurve(just_legend=False)
         if activeCurve is None:
             xproc = None
+            yproc = None
             self.roiWidget.setHeader()
         else:
             x = activeCurve.getXData(copy=False)
@@ -875,6 +882,7 @@ class CurvesROIDockWidget(qt.QDockWidget):
             legend = activeCurve.getLegend()
             idx = numpy.argsort(x, kind='mergesort')
             xproc = numpy.take(x, idx)
+            yproc = numpy.take(y, idx)
             self.roiWidget.setHeader('ROIs of %s' % legend)
 
         for key in roiList:
@@ -891,8 +899,8 @@ class CurvesROIDockWidget(qt.QDockWidget):
                 idx = numpy.nonzero((fromData <= xproc) &
                                     (xproc <= toData))[0]
                 if len(idx):
-                    xw = x[idx]
-                    yw = y[idx]
+                    xw = xproc[idx]
+                    yw = yproc[idx]
                     rawCounts = yw.sum(dtype=numpy.float)
                     deltaX = xw[-1] - xw[0]
                     deltaY = yw[-1] - yw[0]
diff --git a/silx/gui/plot/test/testCurvesROIWidget.py b/silx/gui/plot/test/testCurvesROIWidget.py
index 8e2ad18ea..3c6f2bab5 100644
--- a/silx/gui/plot/test/testCurvesROIWidget.py
+++ b/silx/gui/plot/test/testCurvesROIWidget.py
@@ -106,6 +106,40 @@ class TestCurvesROIWidget(TestCaseQt):
 
             del self.tmpFile
 
+    def testCalculation(self):
+        x = numpy.arange(100.)
+        y = numpy.arange(100.)
+
+        # Add two curves
+        self.plot.addCurve(x, y, legend="positive")
+        self.plot.addCurve(-x, y, legend="negative")
+
+        # Make sure there is an active curve and it is the positive one
+        self.plot.setActiveCurve("positive")
+
+        # Add two ROIs
+        ddict = {}
+        ddict["positive"] = {"from": 10, "to": 20, "type":"X"}
+        ddict["negative"] = {"from": -20, "to": -10, "type":"X"}
+        self.widget.roiWidget.setRois(ddict)
+
+        # And calculate the expected output
+        self.widget.calculateROIs()
+
+        output = self.widget.roiWidget.getRois()
+        self.assertEqual(output["positive"]["rawcounts"],
+                         y[ddict["positive"]["from"]:ddict["positive"]["to"]+1].sum(),
+                         "Calculation failed on positive X coordinates")
+
+        # Set the curve with negative X coordinates as active
+        self.plot.setActiveCurve("negative")
+
+        # the ROIs should have been automatically updated
+        output = self.widget.roiWidget.getRois()
+        selection = numpy.nonzero((-x >= output["negative"]["from"]) & \\
+                                  (-x <= output["negative"]["to"]))[0]
+        self.assertEqual(output["negative"]["rawcounts"],
+                         y[selection].sum(), "Calculation failed on negative X coordinates")
 
 def suite():
     test_suite = unittest.TestSuite()