# pvtrace is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
# 
# pvtrace is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import numpy as np
from external.transformations import translation_matrix, rotation_matrix
import external.transformations as tf
from Trace import Photon
from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm
from Materials import Spectrum

def random_spherecial_vector():
    # This method of calculating isotropic vectors is taken from GNU Scientific Library
    LOOP = True
    while LOOP:
        x = -1. + 2. * np.random.uniform()
        y = -1. + 2. * np.random.uniform()
        s = x**2 + y**2
        if s <= 1.0:
            LOOP = False
            
    z = -1. + 2. * s
    a = 2 * np.sqrt(1 - s)
    x = a * x
    y = a * y
    return np.array([x,y,z])
        
class SimpleSource(object):
    """A light source that will generate photons of a single colour, direction and position."""
    def __init__(self, position=[0,0,0], direction=[0,0,1], wavelength=555, use_random_polarisation=False):
        super(SimpleSource, self).__init__()
        self.position = position
        self.direction = direction
        self.wavelength = wavelength
        self.use_random_polarisation = use_random_polarisation
        self.throw = 0
        self.source_id = "SimpleSource_" + str(id(self))
        
        
    def photon(self):
        
        photon = Photon()
        photon.source = self.source_id
        photon.position = np.array(self.position)
        photon.direction = np.array(self.direction)
        photon.active = True
        photon.wavelength = self.wavelength
        
        # If use_polarisation is set generate a random polarisation vector of the photon
        if self.use_random_polarisation:
            
            # Randomise rotation angle around xy-plane, the transform from +z to the direction of the photon
            vec = random_spherecial_vector()
            vec[2] = 0.
            vec = norm(vec)
            R = rotation_matrix_from_vector_alignment(self.direction, [0,0,1])
            photon.polarisation = transform_direction(vec, R)
            
        else:
            photon.polarisation = None
        
        photon.id = self.throw
        self.throw = self.throw + 1 
        return photon

class Laser(object):
    """A light source that will generate photons of a single colour, direction and position."""
    
    def __init__(self, position=[0,0,0], direction=[0,0,1], wavelength=555, polarisation=None):
        super(Laser, self).__init__()
        self.position = np.array(position)
        self.direction = np.array(direction)
        self.wavelength = wavelength
        assert polarisation != None, "Polarisation of the Laser is not set."
        self.polarisation = np.array(polarisation)
        self.throw = 0
        self.source_id = "LaserSource_" + str(id(self))
        
        
    def photon(self):
        
        photon = Photon()
        photon.source = self.source_id
        photon.position = np.array(self.position)
        photon.direction = np.array(self.direction)
        photon.active = True
        photon.wavelength = self.wavelength
        photon.polarisation = self.polarisation
        photon.id = self.throw
        self.throw = self.throw + 1 
        return photon
        
class PlanarSource(object):
    """A box that emits photons from the top surface (normal), sampled from the spectrum."""
    def __init__(self, spectrum=None, wavelength=555, direction=(0,0,1), length=0.05, width=0.05):
        super(PlanarSource, self).__init__()
        self.spectrum = spectrum
        self.wavelength = wavelength
        self.plane = FinitePlane(length=length, width=width)
        self.length = length
        self.width = width
        # direction is the direction that photons are fired out of the plane in the GLOBAL FRAME.
        # i.e. this is passed directly to the photon to set is's direction
        self.direction = direction
        self.throw = 0
        self.source_id = "PlanarSource_" + str(id(self))
    
    def translate(self, translation):
        self.plane.append_transform(tf.translation_matrix(translation))
    
    def rotate(self, angle, axis):
        self.plane.append_transform(tf.rotation_matrix(angle, axis))
    
    def photon(self):
        photon = Photon()
        photon.source = self.source_id
        photon.id = self.throw
        self.throw = self.throw + 1 
        # Create a point which is on the surface of the finite plane in it's local frame
        x = np.random.uniform(0., self.length)
        y = np.random.uniform(0., self.width)
        local_point = (x, y, 0.)
        
        # Transform the direciton
        photon.position = transform_point(local_point, self.plane.transform)
        photon.direction = self.direction
        photon.active = True
        if self.spectrum != None:
            photon.wavelength = self.spectrum.wavelength_at_probability(np.random.uniform())
        else:
            photon.wavelength = self.wavelength
        return photon

class LensSource(object):
    """
    A source where photons generated in a plane are focused on a line with space tolerance given by variable "focussize".
    The focus line should be perpendicular to the plane normal and aligned with the z-axis. 
    """
    def __init__(self, spectrum = None, wavelength = 555, linepoint=(0,0,0), linedirection=(0,0,1), focussize = 0, planeorigin = (-1,-1,-1), planeextent = (-1,1,1)):
        super(LensSource, self).__init__()
        self.spectrum = spectrum
        self.wavelength = wavelength
        self.planeorigin = planeorigin
        self.planeextent = planeextent
        self.linepoint = np.array(linepoint)
        self.linedirection = np.array(linedirection)
        self.focussize = focussize
        self.throw = 0
        self.source_id = "LensSource_" + str(id(self))

    def photon(self):
        photon = Photon()
        photon.source = self.source_id
        photon.id = self.throw
        self.throw = self.throw + 1 
        
        # Position
        x = np.random.uniform(self.planeorigin[0],self.planeextent[0])
        y = np.random.uniform(self.planeorigin[1],self.planeextent[1])
        z = np.random.uniform(self.planeorigin[2],self.planeextent[2])
        photon.position = np.array((x,y,z))        
        
        # Direction
        focuspoint = np.array((0.,0.,0.))        
        focuspoint[0] = self.linepoint[0] + np.random.uniform(-self.focussize,self.focussize)
        focuspoint[1] = self.linepoint[1] + np.random.uniform(-self.focussize,self.focussize)
        focuspoint[2] = photon.position[2] 
        
        direction = focuspoint - photon.position
        modulus = (direction[0]**2+direction[1]**2+direction[2]**2)**0.5
        photon.direction = direction/modulus
        
        # Wavelength
        if self.spectrum != None:
            photon.wavelength = self.spectrum.wavelength_at_probability(np.random.uniform())
        else:
            photon.wavelength = self.wavelength
            
        return photon   


class LensSourceAngle(object):
    """
    A source where photons generated in a plane are focused on a line with space tolerance given by variable "focussize".
    The focus line should be perpendicular to the plane normal and aligned with the z-axis. 
    For this lense an additional z-boost is added (Angle of incidence in z-direction).
    """
    def __init__(self, spectrum = None, wavelength = 555, linepoint=(0,0,0), linedirection=(0,0,1), angle = 0, focussize = 0, planeorigin = (-1,-1,-1), planeextent = (-1,1,1)):
        super(LensSourceAngle, self).__init__()
        self.spectrum = spectrum
        self.wavelength = wavelength
        self.planeorigin = planeorigin
        self.planeextent = planeextent
        self.linepoint = np.array(linepoint)
        self.linedirection = np.array(linedirection)
        self.focussize = focussize
        self.angle = angle
        self.throw = 0
        self.source_id = "LensSourceAngle_" + str(id(self))
        
    def photon(self):
        photon = Photon()
        
        photon.id = self.throw
        self.throw = self.throw + 1 
        
        # Position
        x = np.random.uniform(self.planeorigin[0],self.planeextent[0])
        y = np.random.uniform(self.planeorigin[1],self.planeextent[1])
        boost = y*np.tan(self.angle)
        z = np.random.uniform(self.planeorigin[2],self.planeextent[2]) - boost
        photon.position = np.array((x,y,z))        
        
        # Direction
        focuspoint = np.array((0.,0.,0.))        
        focuspoint[0] = self.linepoint[0] + np.random.uniform(-self.focussize,self.focussize)
        focuspoint[1] = self.linepoint[1] + np.random.uniform(-self.focussize,self.focussize)
        focuspoint[2] = photon.position[2] + boost
        
        direction = focuspoint - photon.position
        modulus = (direction[0]**2+direction[1]**2+direction[2]**2)**0.5
        photon.direction = direction/modulus
        
        # Wavelength
        if self.spectrum != None:
            photon.wavelength = self.spectrum.wavelength_at_probability(np.random.uniform())
        else:
            photon.wavelength = self.wavelength
            
        return photon   
    

class CylindricalSource(object):
    """
    A source for photons emitted in a random direction and position inside a cylinder(radius, length)
    """
    def __init__(self, spectrum = None, wavelength = 555, radius = 1, length = 10):
        super(CylindricalSource, self).__init__()
        self.spectrum = spectrum
        self.wavelength = wavelength
        self.shape = Cylinder(radius = radius, length = length)
        self.radius = radius
        self.length = length
        self.throw = 0
        self.source_id = "CylindricalSource_" + str(id(self))
                
    def translate(self, translation):
        self.shape.append_transform(tf.translation_matrix(translation))

    def rotate(self, angle, axis):
        self.shape.append_transform(tf.rotation_matrix(angle, axis))

    def photon(self):
        photon = Photon()
        photon.source = self.source_id
        photon.id = self.throw
        self.throw = self.throw + 1 
        
        # Position of emission
        phi = np.random.uniform(0., 2*np.pi)
        r = np.random.uniform(0.,self.radius)        
        
        x = r*np.cos(phi)
        y = r*np.sin(phi) 
        z = np.random.uniform(0.,self.length)
        local_center = (x,y,z)
        
        photon.position = transform_point(local_center, self.shape.transform)
        
        
        # Direction of emission (no need to transform if meant to be isotropic)
        phi = np.random.uniform(0.,2*np.pi)
        theta = np.random.uniform(0.,np.pi)
        
        x = np.cos(phi)*np.sin(theta)
        y = np.sin(phi)*np.sin(theta)
        z = np.cos(theta)
        local_direction = (x,y,z)
        
        photon.direction = local_direction
        
        # Set wavelength of photon
        if self.spectrum != None:
            photon.wavelength = self.spectrum.wavelength_at_probability(np.random.uniform())        
        else:
            photon.wavelength = self.wavelength
            
        # Further initialisation
        photon.active = True
        
        return photon

class PointSource(object):
    """
    A point source that emits randomly in solid angle specified by phimin, ..., thetamax
    """
    def __init__(self, spectrum = None, wavelength = 555, center = (0.,0.,0.), phimin = 0, phimax = 2*np.pi, thetamin = 0, thetamax = np.pi):
        super(PointSource, self).__init__()
        self.spectrum = spectrum
        self.wavelength = wavelength
        self.center = center
        self.phimin = phimin
        self.phimax = phimax
        self.thetamin = thetamin
        self.thetamax = thetamax
        self.throw = 0
        self.source_id = "PointSource_" + str(id(self))

    def photon(self):
        photon = Photon()
        photon.source = self.source_id
        photon.id = self.throw
        self.throw = self.throw + 1
        
        phi = np.random.uniform(self.phimin, self.phimax)
        theta = np.random.uniform(self.thetamin, self.thetamax)
        
        x = np.cos(phi)*np.sin(theta)
        y = np.sin(phi)*np.sin(theta)
        z = np.cos(theta)
        direction = (x,y,z)
        
        transform = tf.translation_matrix((0,0,0))
        point = transform_point(self.center, transform)
        
        photon.direction = direction
        photon.position = point
        
        if self.spectrum != None:
            photon.wavelength = self.spectrum.wavelength_at_probability(np.random.uniform())
        else:
            photon.wavelength = self.wavelength
            
        photon.active = True
        
        return photon

class RadialSource(object):
    """
    A point source that emits at discrete angles theta(i) and phi(i)
    """
    def __init__(self, spectrum = None, wavelength = 555, center = (0.,0.,0.), phimin = 0, phimax = 2*np.pi, thetamin = 0, thetamax = np.pi, spacing=20):
        super(RadialSource, self).__init__()
        self.spectrum = spectrum
        self.wavelength = wavelength
        self.center = center
        self.phimin = phimin
        self.phimax = phimax
        self.thetamin = thetamin
        self.thetamax = thetamax
        self.spacing = spacing
        self.throw = 0
        self.source_id = "RadialSource_" + str(id(self))

    def photon(self):
        photon = Photon()
        
        photon.source = self.source_id
        photon.id = self.throw
        self.throw = self.throw + 1
        
        intphi = np.random.randint(1, self.spacing+1)        
        inttheta = np.random.randint(1, self.spacing+1)
        
        phi = intphi*(self.phimax-self.phimin)/self.spacing
        if self.thetamin == self.thetamax:
            theta = self.thetamin
        else:
            theta = inttheta*(self.thetamax-self.thetamin)/self.spacing
        
        x = np.cos(phi)*np.sin(theta)      
        y = np.sin(phi)*np.sin(theta) 
        z = np.cos(theta)
        direction = (x,y,z)
        
        transform = tf.translation_matrix((0,0,0))
        point = transform_point(self.center, transform)
        
        photon.direction = direction
        photon.position = point
        
        if self.spectrum != None:
            photon.wavelength = self.spectrum.wavelength_at_probability(np.random.uniform())
        else:
            photon.wavelength = self.wavelength
            
        photon.active = True
        
        return photon

[((35, 11, 35, 28), 'numpy.array', 'np.array', ({(35, 20, 35, 27): '[x, y, z]'}, {}), '([x, y, z])', True, 'import numpy as np\n'), ((32, 12, 32, 26), 'numpy.sqrt', 'np.sqrt', ({(32, 20, 32, 25): '(1 - s)'}, {}), '(1 - s)', True, 'import numpy as np\n'), ((51, 17, 51, 25), 'Trace.Photon', 'Photon', ({}, {}), '()', False, 'from Trace import Photon\n'), ((53, 26, 53, 49), 'numpy.array', 'np.array', ({(53, 35, 53, 48): 'self.position'}, {}), '(self.position)', True, 'import numpy as np\n'), ((54, 27, 54, 51), 'numpy.array', 'np.array', ({(54, 36, 54, 50): 'self.direction'}, {}), '(self.direction)', True, 'import numpy as np\n'), ((80, 24, 80, 42), 'numpy.array', 'np.array', ({(80, 33, 80, 41): 'position'}, {}), '(position)', True, 'import numpy as np\n'), ((81, 25, 81, 44), 'numpy.array', 'np.array', ({(81, 34, 81, 43): 'direction'}, {}), '(direction)', True, 'import numpy as np\n'), ((84, 28, 84, 50), 'numpy.array', 'np.array', ({(84, 37, 84, 49): 'polarisation'}, {}), '(polarisation)', True, 'import numpy as np\n'), ((91, 17, 91, 25), 'Trace.Photon', 'Photon', ({}, {}), '()', False, 'from Trace import Photon\n'), ((93, 26, 93, 49), 'numpy.array', 'np.array', ({(93, 35, 93, 48): 'self.position'}, {}), '(self.position)', True, 'import numpy as np\n'), ((94, 27, 94, 51), 'numpy.array', 'np.array', ({(94, 36, 94, 50): 'self.direction'}, {}), '(self.direction)', True, 'import numpy as np\n'), ((108, 21, 108, 60), 'Geometry.FinitePlane', 'FinitePlane', (), '', False, 'from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm\n'), ((124, 17, 124, 25), 'Trace.Photon', 'Photon', ({}, {}), '()', False, 'from Trace import Photon\n'), ((129, 12, 129, 46), 'numpy.random.uniform', 'np.random.uniform', ({(129, 30, 129, 32): '0.0', (129, 34, 129, 45): 'self.length'}, {}), '(0.0, self.length)', True, 'import numpy as np\n'), ((130, 12, 130, 45), 'numpy.random.uniform', 'np.random.uniform', ({(130, 30, 130, 32): '0.0', (130, 34, 130, 44): 'self.width'}, {}), '(0.0, self.width)', True, 'import numpy as np\n'), ((134, 26, 134, 76), 'Geometry.transform_point', 'transform_point', ({(134, 42, 134, 53): 'local_point', (134, 55, 134, 75): 'self.plane.transform'}, {}), '(local_point, self.plane.transform)', False, 'from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm\n'), ((154, 25, 154, 44), 'numpy.array', 'np.array', ({(154, 34, 154, 43): 'linepoint'}, {}), '(linepoint)', True, 'import numpy as np\n'), ((155, 29, 155, 52), 'numpy.array', 'np.array', ({(155, 38, 155, 51): 'linedirection'}, {}), '(linedirection)', True, 'import numpy as np\n'), ((161, 17, 161, 25), 'Trace.Photon', 'Photon', ({}, {}), '()', False, 'from Trace import Photon\n'), ((167, 12, 167, 70), 'numpy.random.uniform', 'np.random.uniform', ({(167, 30, 167, 49): 'self.planeorigin[0]', (167, 50, 167, 69): 'self.planeextent[0]'}, {}), '(self.planeorigin[0], self.planeextent[0])', True, 'import numpy as np\n'), ((168, 12, 168, 70), 'numpy.random.uniform', 'np.random.uniform', ({(168, 30, 168, 49): 'self.planeorigin[1]', (168, 50, 168, 69): 'self.planeextent[1]'}, {}), '(self.planeorigin[1], self.planeextent[1])', True, 'import numpy as np\n'), ((169, 12, 169, 70), 'numpy.random.uniform', 'np.random.uniform', ({(169, 30, 169, 49): 'self.planeorigin[2]', (169, 50, 169, 69): 'self.planeextent[2]'}, {}), '(self.planeorigin[2], self.planeextent[2])', True, 'import numpy as np\n'), ((170, 26, 170, 43), 'numpy.array', 'np.array', ({(170, 35, 170, 42): '(x, y, z)'}, {}), '((x, y, z))', True, 'import numpy as np\n'), ((173, 21, 173, 41), 'numpy.array', 'np.array', ({(173, 30, 173, 40): '(0.0, 0.0, 0.0)'}, {}), '((0.0, 0.0, 0.0))', True, 'import numpy as np\n'), ((203, 25, 203, 44), 'numpy.array', 'np.array', ({(203, 34, 203, 43): 'linepoint'}, {}), '(linepoint)', True, 'import numpy as np\n'), ((204, 29, 204, 52), 'numpy.array', 'np.array', ({(204, 38, 204, 51): 'linedirection'}, {}), '(linedirection)', True, 'import numpy as np\n'), ((211, 17, 211, 25), 'Trace.Photon', 'Photon', ({}, {}), '()', False, 'from Trace import Photon\n'), ((217, 12, 217, 70), 'numpy.random.uniform', 'np.random.uniform', ({(217, 30, 217, 49): 'self.planeorigin[0]', (217, 50, 217, 69): 'self.planeextent[0]'}, {}), '(self.planeorigin[0], self.planeextent[0])', True, 'import numpy as np\n'), ((218, 12, 218, 70), 'numpy.random.uniform', 'np.random.uniform', ({(218, 30, 218, 49): 'self.planeorigin[1]', (218, 50, 218, 69): 'self.planeextent[1]'}, {}), '(self.planeorigin[1], self.planeextent[1])', True, 'import numpy as np\n'), ((221, 26, 221, 43), 'numpy.array', 'np.array', ({(221, 35, 221, 42): '(x, y, z)'}, {}), '((x, y, z))', True, 'import numpy as np\n'), ((224, 21, 224, 41), 'numpy.array', 'np.array', ({(224, 30, 224, 40): '(0.0, 0.0, 0.0)'}, {}), '((0.0, 0.0, 0.0))', True, 'import numpy as np\n'), ((250, 21, 250, 63), 'Geometry.Cylinder', 'Cylinder', (), '', False, 'from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm\n'), ((263, 17, 263, 25), 'Trace.Photon', 'Photon', ({}, {}), '()', False, 'from Trace import Photon\n'), ((269, 14, 269, 44), 'numpy.random.uniform', 'np.random.uniform', ({(269, 32, 269, 34): '0.0', (269, 36, 269, 43): '2 * np.pi'}, {}), '(0.0, 2 * np.pi)', True, 'import numpy as np\n'), ((270, 12, 270, 45), 'numpy.random.uniform', 'np.random.uniform', ({(270, 30, 270, 32): '0.0', (270, 33, 270, 44): 'self.radius'}, {}), '(0.0, self.radius)', True, 'import numpy as np\n'), ((274, 12, 274, 45), 'numpy.random.uniform', 'np.random.uniform', ({(274, 30, 274, 32): '0.0', (274, 33, 274, 44): 'self.length'}, {}), '(0.0, self.length)', True, 'import numpy as np\n'), ((277, 26, 277, 77), 'Geometry.transform_point', 'transform_point', ({(277, 42, 277, 54): 'local_center', (277, 56, 277, 76): 'self.shape.transform'}, {}), '(local_center, self.shape.transform)', False, 'from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm\n'), ((281, 14, 281, 43), 'numpy.random.uniform', 'np.random.uniform', ({(281, 32, 281, 34): '0.0', (281, 35, 281, 42): '2 * np.pi'}, {}), '(0.0, 2 * np.pi)', True, 'import numpy as np\n'), ((282, 16, 282, 43), 'numpy.random.uniform', 'np.random.uniform', ({(282, 34, 282, 36): '0.0', (282, 37, 282, 42): 'np.pi'}, {}), '(0.0, np.pi)', True, 'import numpy as np\n'), ((286, 12, 286, 25), 'numpy.cos', 'np.cos', ({(286, 19, 286, 24): 'theta'}, {}), '(theta)', True, 'import numpy as np\n'), ((319, 17, 319, 25), 'Trace.Photon', 'Photon', ({}, {}), '()', False, 'from Trace import Photon\n'), ((324, 14, 324, 57), 'numpy.random.uniform', 'np.random.uniform', ({(324, 32, 324, 43): 'self.phimin', (324, 45, 324, 56): 'self.phimax'}, {}), '(self.phimin, self.phimax)', True, 'import numpy as np\n'), ((325, 16, 325, 63), 'numpy.random.uniform', 'np.random.uniform', ({(325, 34, 325, 47): 'self.thetamin', (325, 49, 325, 62): 'self.thetamax'}, {}), '(self.thetamin, self.thetamax)', True, 'import numpy as np\n'), ((329, 12, 329, 25), 'numpy.cos', 'np.cos', ({(329, 19, 329, 24): 'theta'}, {}), '(theta)', True, 'import numpy as np\n'), ((332, 20, 332, 50), 'external.transformations.translation_matrix', 'tf.translation_matrix', ({(332, 42, 332, 49): '(0, 0, 0)'}, {}), '((0, 0, 0))', True, 'import external.transformations as tf\n'), ((333, 16, 333, 55), 'Geometry.transform_point', 'transform_point', ({(333, 32, 333, 43): 'self.center', (333, 45, 333, 54): 'transform'}, {}), '(self.center, transform)', False, 'from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm\n'), ((365, 17, 365, 25), 'Trace.Photon', 'Photon', ({}, {}), '()', False, 'from Trace import Photon\n'), ((371, 17, 371, 53), 'numpy.random.randint', 'np.random.randint', ({(371, 35, 371, 36): '1', (371, 38, 371, 52): 'self.spacing + 1'}, {}), '(1, self.spacing + 1)', True, 'import numpy as np\n'), ((372, 19, 372, 55), 'numpy.random.randint', 'np.random.randint', ({(372, 37, 372, 38): '1', (372, 40, 372, 54): 'self.spacing + 1'}, {}), '(1, self.spacing + 1)', True, 'import numpy as np\n'), ((382, 12, 382, 25), 'numpy.cos', 'np.cos', ({(382, 19, 382, 24): 'theta'}, {}), '(theta)', True, 'import numpy as np\n'), ((385, 20, 385, 50), 'external.transformations.translation_matrix', 'tf.translation_matrix', ({(385, 42, 385, 49): '(0, 0, 0)'}, {}), '((0, 0, 0))', True, 'import external.transformations as tf\n'), ((386, 16, 386, 55), 'Geometry.transform_point', 'transform_point', ({(386, 32, 386, 43): 'self.center', (386, 45, 386, 54): 'transform'}, {}), '(self.center, transform)', False, 'from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm\n'), ((64, 18, 64, 27), 'Geometry.norm', 'norm', ({(64, 23, 64, 26): 'vec'}, {}), '(vec)', False, 'from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm\n'), ((65, 16, 65, 78), 'Geometry.rotation_matrix_from_vector_alignment', 'rotation_matrix_from_vector_alignment', ({(65, 54, 65, 68): 'self.direction', (65, 70, 65, 77): '[0, 0, 1]'}, {}), '(self.direction, [0, 0, 1])', False, 'from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm\n'), ((66, 34, 66, 61), 'Geometry.transform_direction', 'transform_direction', ({(66, 54, 66, 57): 'vec', (66, 59, 66, 60): 'R'}, {}), '(vec, R)', False, 'from Geometry import Box, Cylinder, FinitePlane, transform_point, transform_direction, rotation_matrix_from_vector_alignment, norm\n'), ((118, 36, 118, 70), 'external.transformations.translation_matrix', 'tf.translation_matrix', ({(118, 58, 118, 69): 'translation'}, {}), '(translation)', True, 'import external.transformations as tf\n'), ((121, 36, 121, 67), 'external.transformations.rotation_matrix', 'tf.rotation_matrix', ({(121, 55, 121, 60): 'angle', (121, 62, 121, 66): 'axis'}, {}), '(angle, axis)', True, 'import external.transformations as tf\n'), ((174, 44, 174, 93), 'numpy.random.uniform', 'np.random.uniform', ({(174, 62, 174, 77): '(-self.focussize)', (174, 78, 174, 92): 'self.focussize'}, {}), '(-self.focussize, self.focussize)', True, 'import numpy as np\n'), ((175, 44, 175, 93), 'numpy.random.uniform', 'np.random.uniform', ({(175, 62, 175, 77): '(-self.focussize)', (175, 78, 175, 92): 'self.focussize'}, {}), '(-self.focussize, self.focussize)', True, 'import numpy as np\n'), ((219, 18, 219, 36), 'numpy.tan', 'np.tan', ({(219, 25, 219, 35): 'self.angle'}, {}), '(self.angle)', True, 'import numpy as np\n'), ((220, 12, 220, 70), 'numpy.random.uniform', 'np.random.uniform', ({(220, 30, 220, 49): 'self.planeorigin[2]', (220, 50, 220, 69): 'self.planeextent[2]'}, {}), '(self.planeorigin[2], self.planeextent[2])', True, 'import numpy as np\n'), ((225, 44, 225, 93), 'numpy.random.uniform', 'np.random.uniform', ({(225, 62, 225, 77): '(-self.focussize)', (225, 78, 225, 92): 'self.focussize'}, {}), '(-self.focussize, self.focussize)', True, 'import numpy as np\n'), ((226, 44, 226, 93), 'numpy.random.uniform', 'np.random.uniform', ({(226, 62, 226, 77): '(-self.focussize)', (226, 78, 226, 92): 'self.focussize'}, {}), '(-self.focussize, self.focussize)', True, 'import numpy as np\n'), ((257, 36, 257, 70), 'external.transformations.translation_matrix', 'tf.translation_matrix', ({(257, 58, 257, 69): 'translation'}, {}), '(translation)', True, 'import external.transformations as tf\n'), ((260, 36, 260, 67), 'external.transformations.rotation_matrix', 'tf.rotation_matrix', ({(260, 55, 260, 60): 'angle', (260, 62, 260, 66): 'axis'}, {}), '(angle, axis)', True, 'import external.transformations as tf\n'), ((272, 14, 272, 25), 'numpy.cos', 'np.cos', ({(272, 21, 272, 24): 'phi'}, {}), '(phi)', True, 'import numpy as np\n'), ((273, 14, 273, 25), 'numpy.sin', 'np.sin', ({(273, 21, 273, 24): 'phi'}, {}), '(phi)', True, 'import numpy as np\n'), ((284, 12, 284, 23), 'numpy.cos', 'np.cos', ({(284, 19, 284, 22): 'phi'}, {}), '(phi)', True, 'import numpy as np\n'), ((284, 24, 284, 37), 'numpy.sin', 'np.sin', ({(284, 31, 284, 36): 'theta'}, {}), '(theta)', True, 'import numpy as np\n'), ((285, 12, 285, 23), 'numpy.sin', 'np.sin', ({(285, 19, 285, 22): 'phi'}, {}), '(phi)', True, 'import numpy as np\n'), ((285, 24, 285, 37), 'numpy.sin', 'np.sin', ({(285, 31, 285, 36): 'theta'}, {}), '(theta)', True, 'import numpy as np\n'), ((327, 12, 327, 23), 'numpy.cos', 'np.cos', ({(327, 19, 327, 22): 'phi'}, {}), '(phi)', True, 'import numpy as np\n'), ((327, 24, 327, 37), 'numpy.sin', 'np.sin', ({(327, 31, 327, 36): 'theta'}, {}), '(theta)', True, 'import numpy as np\n'), ((328, 12, 328, 23), 'numpy.sin', 'np.sin', ({(328, 19, 328, 22): 'phi'}, {}), '(phi)', True, 'import numpy as np\n'), ((328, 24, 328, 37), 'numpy.sin', 'np.sin', ({(328, 31, 328, 36): 'theta'}, {}), '(theta)', True, 'import numpy as np\n'), ((380, 12, 380, 23), 'numpy.cos', 'np.cos', ({(380, 19, 380, 22): 'phi'}, {}), '(phi)', True, 'import numpy as np\n'), ((380, 24, 380, 37), 'numpy.sin', 'np.sin', ({(380, 31, 380, 36): 'theta'}, {}), '(theta)', True, 'import numpy as np\n'), ((381, 12, 381, 23), 'numpy.sin', 'np.sin', ({(381, 19, 381, 22): 'phi'}, {}), '(phi)', True, 'import numpy as np\n'), ((381, 24, 381, 37), 'numpy.sin', 'np.sin', ({(381, 31, 381, 36): 'theta'}, {}), '(theta)', True, 'import numpy as np\n'), ((25, 23, 25, 42), 'numpy.random.uniform', 'np.random.uniform', ({}, {}), '()', True, 'import numpy as np\n'), ((26, 23, 26, 42), 'numpy.random.uniform', 'np.random.uniform', ({}, {}), '()', True, 'import numpy as np\n'), ((138, 72, 138, 91), 'numpy.random.uniform', 'np.random.uniform', ({}, {}), '()', True, 'import numpy as np\n'), ((184, 72, 184, 91), 'numpy.random.uniform', 'np.random.uniform', ({}, {}), '()', True, 'import numpy as np\n'), ((235, 72, 235, 91), 'numpy.random.uniform', 'np.random.uniform', ({}, {}), '()', True, 'import numpy as np\n'), ((293, 72, 293, 91), 'numpy.random.uniform', 'np.random.uniform', ({}, {}), '()', True, 'import numpy as np\n'), ((339, 72, 339, 91), 'numpy.random.uniform', 'np.random.uniform', ({}, {}), '()', True, 'import numpy as np\n'), ((392, 72, 392, 91), 'numpy.random.uniform', 'np.random.uniform', ({}, {}), '()', True, 'import numpy as np\n')]