Repository: DCC-Lab/RayTracing

Issue:
Rays() accepts objects from every type
This can cause further problems. In `__init__` itself it is not a big deal (except for coherence and logic) if `Rays` receive a string of nonsense or a list of numbers, but other methods will inevitably fail with exceptions of different types.

Discussion:

Patch:
diff --git a/raytracing/rays.py b/raytracing/rays.py
index 11de6ad..1963e25 100644
--- a/raytracing/rays.py
+++ b/raytracing/rays.py
@@ -4,20 +4,36 @@ import matplotlib.pyplot as plt
 import pickle
 import time
 import os
-
-""" A group of rays kept as a list, to be used as a starting
-point (i.e. an object) or as a cumulative detector (i.e. at an image
-or output plane) for ImagingPath, MatrixGroup or any tracing function.
-Subclasses can provide a computed ray for Monte Carlo simulation.
+import collections.abc as collections
+
+""" A source or a detector of rays
+
+We can obtain intensity distributions at from given plane by propagating
+many rays and collecting them at another plane. `Rays` is the base class
+that provides the essential mechanisms to obtain histograms on a list 
+of rays (created or collected). This list of rays is a property of the base
+class.  Subclasses are specific to a given ray distribution (Lambertian for 
+instance) and will create each ray on demand, then store them as they go
+in the rays list. 
+
+It is an iterable object, which means it can be used in an expression 
+like `for ray in rays:` which is convenient both when propagating rays 
+or when analysing the resulting rays that reached a plane in ImagingPath, 
+MatrixGroup or any tracing function. 
 """
 
-
 class Rays:
     def __init__(self, rays=None):
         if rays is None:
             self.rays = []
         else:
-            self.rays = rays
+            if isinstance(rays, collections.Iterable):
+                if all([isinstance(ray, Ray) for ray in rays]):
+                    self.rays = list(rays)
+                else:
+                    raise TypeError("'rays' elements must be of type Ray.")
+            else:
+                raise TypeError("'rays' must be iterable (i.e. a list or a tuple of Ray).")
 
         self.iteration = 0
         self.progressLog = 10000
@@ -160,6 +176,8 @@ class Rays:
         return self.rays[item]
 
     def append(self, ray):
+        if not isinstance(ray, Ray):
+            raise TypeError("'ray' must be a 'Ray' object.")
         if self.rays is not None:
             self.rays.append(ray)
 
@@ -214,21 +232,6 @@ class Rays:
     # https://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm
     # and https://stackoverflow.com/questions/3122049/drawing-an-anti-aliased-line-with-thepython-imaging-library
 
-    # @property
-    # def intensityError(self):
-    #     return list(map(lambda x : sqrt(x), self.distribution))
-
-    # @property
-    # def normalizedIntensity(self):
-    #     maxValue = max(self.values)
-    #     return list(map(lambda x : x/maxValue, self.distribution))
-
-    # @property
-    # def normalizedIntensityError(self):
-    #     maxValue = max(self.distribution)
-    #     return list(map(lambda x : x/maxValue, self.error))
-
-
 class UniformRays(Rays):
     def __init__(self, yMax=1.0, yMin=None, thetaMax=pi / 2, thetaMin=None, M=100, N=100):
         self.yMax = yMax


Test patch:
diff --git a/raytracing/tests/testsRays.py b/raytracing/tests/testsRays.py
index 9bb4c7c..1638b29 100644
--- a/raytracing/tests/testsRays.py
+++ b/raytracing/tests/testsRays.py
@@ -7,6 +7,31 @@ inf = float("+inf")
 
 class TestRays(unittest.TestCase):
 
+    def testRaysInitDifferentInputs(self):
+        listOfRays = [Ray(), Ray(1, 1), Ray(1, -2), Ray(0, -1)]
+        tupleOfRays = tuple(listOfRays)
+        npArrayOfRays = array(listOfRays)
+        raysFromList = Rays(listOfRays)
+        raysFromTuple = Rays(tupleOfRays)
+        raysFromArray = Rays(npArrayOfRays)
+        rays = Rays(listOfRays)
+        self.assertListEqual(raysFromList.rays, listOfRays)
+        self.assertListEqual(raysFromTuple.rays, listOfRays)
+        self.assertListEqual(raysFromArray.rays, listOfRays)
+        self.assertListEqual(Rays(rays).rays, listOfRays)
+
+        with self.assertRaises(TypeError) as error:
+            # This should raise an TypeError exception
+            Rays("Ray(), Ray(1), Ray(1,1)")
+
+        with self.assertRaises(TypeError) as error:
+            # This should raise an TypeError exception
+            Rays([Ray(), [1, 2], Ray()])
+
+        with self.assertRaises(TypeError) as error:
+            # This should raise an TypeError exception
+            Rays(Matrix())
+
     def testRayCountHist(self):
         r = Rays([Ray()])
         init = r.rayCountHistogram()