#! /usr/bin/env python

import argparse
import copy
import glob
import math
import os
import sys
import time

from paraview.simple import *


KnownParts = [ 'bdy', 'brk-frt', 'brk-rr', 'susp-frt', 'susp-rr', 'floor', 'diff',  'frt-whl', 'rr-whl', 'fw', 'intnl', 'rw' ]

##########################################################################################################################################################################################################
###           UTILITY STATIC FUNCTIONS
def isInt(num):
    """
    Check that a string is an integer number
    @param num: number to be checked
    @returns: True or False
    """
    res = False
    if isinstance(num,str):
        try:
            int(num)
        except:
            res = False
        else:
            res = True
    elif isinstance(num,int):
        res = True
    elif isinstance(num,float):
        if abs(int(num)-num) < VSMALL:
            res = True
        else:
            res = False
    return res
### 
def isNum(string):
    """
    Check that a string is a number
    @param string: string to be checked
    @returns: True or False
    """
    try:
        float(string)
    except:
        res = False
    else:
        res = True
    return res
###
def chkMkDir(dir):
    """
    Generic function used to check if a directory already exists. 
    If the directory exists and is not a directory, the file will be removed. 
    Eventually, the directory will be created unless the directory already exists.
    
    @param dir: Directory to be created.
    """
    
    ### Check if the directory does not exist and is not a directory (for instance a regular file)
    if (not os.path.exists(dir) or not os.path.isdir(dir)):
        if(os.path.exists(dir) and not os.path.isdir(dir)):
            print 'Warning: Directory '+dir+' already exists but is not a regular directory. Removing it and generating a directory instead.'
            os.remove(dir)
        ### Try to generate the directory and ignore any OS errors.
        try:
            os.makedirs(dir)
        except OSError:
            pass
    ### Else directory dir exists and nothing needs to be done
    return
###
def MakeBlueToRedLT(min,max):
    # Define RGB points. These are tuples of 4 values. First one is
    # the scalar values, the other 3 the RGB values.
    nScalars = 20
    delta = (max - min) / float(nScalars - 1)
    rgbPoints = [ min           , 0.00, 0.00, 1.00,
                  min +    delta, 0.00, 0.45, 1.00,
                  min +  2*delta, 0.00, 0.63, 1.00,
                  min +  3*delta, 0.00, 0.77, 1.00,
                  min +  4*delta, 0.00, 0.89, 1.00,
                  min +  5*delta, 0.00, 1.00, 1.00,
                  min +  6*delta, 0.00, 1.00, 0.86,
                  min +  7*delta, 0.00, 1.00, 0.70,
                  min +  8*delta, 0.00, 1.00, 0.49,
                  min +  9*delta, 0.00, 1.00, 0.00,
                  min + 10*delta, 0.49, 1.00, 0.00,
                  min + 11*delta, 0.70, 1.00, 0.00,
                  min + 12*delta, 0.86, 1.00, 0.00,
                  min + 13*delta, 1.00, 1.00, 0.00,
                  min + 14*delta, 1.00, 0.91, 0.00,
                  min + 15*delta, 1.00, 0.82, 0.00,
                  min + 16*delta, 1.00, 0.70, 0.00,                 
                  min + 17*delta, 1.00, 0.58, 0.00,
                  min + 18*delta, 1.00, 0.40, 0.00,
                  min + 19*delta, 1.00, 0.00, 0.00 ]

    return paraview.simple.CreateLookupTable(RGBPoints=rgbPoints, ColorSpace="RGB", NumberOfTableValues=32)
##########################################################################################################################################################################################################

class cameraSettings(object):
    """
    Very basic class to handle rendering camera settings
    """    
    
    def __init__(self, viewUp = [], focalPoint = [], position = [], zoom = 1, clippingRange = [], parallelProjection = False, computePlaneNormal = False, camResetBeforeZoom = False, baseElevation = 0 ):
        self.ViewUp = viewUp
        self.ClippingRange = clippingRange
        self.FocalPoint = focalPoint
        self.Elevation = baseElevation
        self.Position = position
        self.ParallelProjection = parallelProjection
        self.Zoom = zoom
        self.ComputePlaneNormal = computePlaneNormal
        self.ResetCameraBeforeZoom = camResetBeforeZoom
        return
    
    def addPositionOffset(self, Offset):
        for i,val in enumerate(Offset):
            self.Position[i] += val
        return
    
    def addFocalPointOffset(self, Offset):
        for i,val in enumerate(Offset):
            self.FocalPoint[i] += val
        return

    def addElevationOffset(self,Offset):
        self.Elevation += Offset
        return
    
    def setPosition(self, Position):
        for i,val in enumerate(Position):
            self.Position[i] = val
        return
    
    def setFocalPoint(self, fp):
        for i,val in enumerate(fp):
            self.FocalPoint[i] = val
        return
    
    def setElevation(self, theta):
        self.Elevation = theta
        return
##########################################################################################################################################################################################################

class Flushfile(object):
    """
    Class for unbuffered file output
    """
    def __init__(self, fd):
        self.fd = fd

    def write(self, x):
        ret=self.fd.write(x)
        self.fd.flush()
        return ret

    def writelines(self, lines):
        ret=self.writelines(line)
        self.fd.flush()
        return ret

    def flush(self):
        return self.fd.flush

    def close(self):
        return self.fd.close()

    def fileno(self):
        return self.fd.fileno()
    
##########################################################################################################################################################################################################

class PredefinedPVRenderView(object):
    
    """
    Generic class for defining and handling predefined PV rendering views
    """
    
    def __init__(self, name = None, specificCameraSettings = None, filePrefix= None, valueTag = None, imageScaleUp = 1):
        
        ### Name of the predefined view
        self.name = ''
        if (isinstance(name,str)):
            self.name = name
        
        if (specificCameraSettings):
            if (isinstance(specificCameraSettings, cameraSettings)):
                self.cameraSettings = specificCameraSettings
            else:
                raise TypeError, 'Expecting object of type cameraSettings but got an object of type '+str(type(specificCameraSettings))
        else:
            self.cameraSettings                       = cameraSettings()
            self.cameraSettings.ViewUp                = [1,  0, 0]
            self.cameraSettings.FocalPoint            = [0.0, 0.0, 0.0]
            self.cameraSettings.Position              = [0.0, 0.0, 1.0]
            self.cameraSettings.ResetCameraBeforeZoom = True
        
        self.filePrefix = ''
        if (filePrefix): self.setPrefix(filePrefix)
        
        self.valueTag = None
        if (valueTag): 
            if (isinstance(valueTag,str)):
                self.valueTag = valueTag
                
        ### Image scale up factor     
        self.imageScaleUp  = imageScaleUp
        
        return
    
    def setPrefix(self, value):
        if (isinstance(value, str)):
            self.filePrefix = value
        else:
            raise TypeError, "PredefinedRenderView::setPrefix : Wrong type for input parameter value. Expected type is str, but got "+str(type(value))+" instead." 
        return
    
    def setImageScaleUp(self, value):
        if (isInt(value) and value > 0):
            self.imageScaleUp = value
        else:
            raise ValueError, "PredefinedRenderView::setImageScaleUp : Wrong type of input for parameter value. Expected integer value > 0, but got "+str(value)+" instead." 
        return
    
    def generate(self, view, toDir):
        """
        Method to generate the rendering to image file or to screen.
        """    
        view.ResetCamera()
        camera = view.GetActiveCamera()
        
        ################################################################
        ###        SETUP CAMERA ACCORDING TO PREDEFINED SETTINGS    ####
        ################################################################
          
        camera.SetPosition(self.cameraSettings.Position)
        camera.SetFocalPoint(self.cameraSettings.FocalPoint)
        camera.Elevation(self.cameraSettings.Elevation)
        
        ### Compute plane normal
        if (self.cameraSettings.ComputePlaneNormal):
            camera.ComputeViewPlaneNormal()
            camera.SetViewUp(self.cameraSettings.ViewUp)
            camera.OrthogonalizeViewUp()
        else:
            camera.SetViewUp(self.cameraSettings.ViewUp)
             
        ### Set parallel projection on or off
        if (self.cameraSettings.ParallelProjection):
            camera.ParallelProjectionOn()
        else:
            camera.ParallelProjectionOff()
        
        ### Set default or custom camera clipping range
        if (self.cameraSettings.ClippingRange):
            camera.SetClippingRange(self.cameraSettings.ClippingRange)
        else:
            camera.SetClippingRange(0.01,1000.01)     
                
        ### Check if a camera reset is required before defining the zoom factor
        zoomLevel = self.cameraSettings.Zoom
        if (self.cameraSettings.ResetCameraBeforeZoom):
            view.ResetCamera()
            view.GetActiveCamera().Zoom(zoomLevel)
        else:
            view.GetActiveCamera().Zoom(zoomLevel)
            
        ################################################################
        ###                PERFORM THE RENDERING                     ###
        ################################################################
        
        if (toDir):
            ### Dump screen output to file
            chkMkDir(toDir)
        
            ### Define filename to output generate JPEG files to.
            fileName = str(self.filePrefix)
            if (self.valueTag): fileName += str(self.valueTag)
            fileName += ".jpg"
            view.WriteImage(os.path.join(toDir,fileName), "vtkJPEGWriter", self.imageScaleUp)
        
        else:
            ### Perform output to screen
            view.StillRender() 
            
        ### Unzoom (else the camera will stay on this zoom level)    
        view.GetActiveCamera().Zoom(1./zoomLevel)

        return
    
##########################################################################################################################################################################################################

class Predefined3DTethaPhiPVRenderView(PredefinedPVRenderView):    
      
    """
    Class derived from PredefinedPVRenderView. Performs the rendering following a camera moving on an ellipsoid of revolution.
    """
    
    def __init__(self, *args):
        
        PredefinedPVRenderView.__init__(self, *args)
        
        self.ThetaRange = []     ### Range of angular positions on the elliptical trajectory
        self.PhiRange   = []     ### Range of angular positions around rotation axis defining the elliptical trajectory
        self.SmallAxis  = 0.0    ### Small axis of the ellipse
        self.BigAxis    = 0.0    ### Big axis of the ellipse
        
        return
    
    def setBigAxis(self, a):
        if isNum(a):
            if (a > 0):
                self.BigAxis = a
            else:
                raise ValueError, 'A numeric value > 0 is required.'
        else:
            raise TypeError, 'Expecting a numeric value (float or int), but received an object of type '+str(type(a))+' instead.'
    
    def setSmallAxis(self,a):
        if isNum(a):
            if (a > 0):
                self.SmallAxis = a
            else:
                raise ValueError, 'A numeric value > 0 is required.'
        else:
            raise TypeError, 'Expecting a numeric value (float or int), but received an object of type '+str(type(a))+' instead.'

    def setThetaRange(self, valuesList):
        if (isinstance(valuesList,list)):
            ### Check all values in the list, just to make sure that they numerical values
            for val in valuesList:
                if not(isNum(val)):
                    raise ValueError, 'All elements of the list need to be numerical values. Found an object of type '+str(type(val))+' instead.'
            self.ThetaRange = valuesList
        else:
            raise TypeError, 'Expecting a list of data, but received an object of type'+str(type(valuesList))+' instead.'
    
    def setPhiRange(self, valuesList):
        if (isinstance(valuesList,list)):
            ### Check all values in the list, just to make sure that they numerical values
            for val in valuesList:
                if not(isNum(val)):
                    raise ValueError, 'All elements of the list need to be numerical values. Found an object of type '+str(type(val))+' instead.'
            self.PhiRange = valuesList
        else:
            raise TypeError, 'Expecting a list of data, but received an object of type'+str(type(valuesList))+' instead.'
        
    def generate(self,view,toDir):
        
        if ((self.ThetaRange) or (self.PhiRange)):
            
            if ((self.SmallAxis > 0) and (self.BigAxis > 0)):
            
                #### Store original camera settings 
                CameraSettings0 = copy.deepcopy(self.cameraSettings)
        
                for theta in self.ThetaRange:
                    for phi in self.PhiRange:
            
                        self.valueTag = "theta=%+.2f--phi=%+.2f" % (theta,phi)
                        x = self.BigAxis   * math.cos(math.radians(theta)) * math.cos(math.radians(phi))
                        y = self.SmallAxis * math.sin(math.radians(theta)) * math.cos(math.radians(phi))
                        z = self.BigAxis   * math.sin(math.radians(phi))  
                        self.cameraSettings.addPositionOffset([x,y,z])
                
                        PredefinedPVRenderView.generate(self, view,toDir)
                
                        #### Restore original camera settings
                        self.cameraSettings.setPosition(CameraSettings0.Position)
                        self.valueTag = None
            else:
                raise ValueError, 'Requested images generation for an elliptic trajectory which is reduced to either a line or a point.'
        else:
            raise ValueError, 'Requested images generation for a range of angular values which is empty.'
        
        return
    
##########################################################################################################################################################################################################
class Predefined3DHiResPVRenderView(Predefined3DTethaPhiPVRenderView):
    """
    Class derived from Predefined3DThetaPhiPVRenderView. Performs the rendering with 3 times the standard resolution.
    """
    def __init__(self, thetaRange = [], phiRange = [], BigAxis = 1, SmallAxis = 1):
        
        camSettings = cameraSettings()
        camSettings.ViewUp        = [0, 0, 1]
        camSettings.FocalPoint    = [1.5, 0.0, 0.5]
        camSettings.Position      = [1.5, 0.0, 0.5]
        camSettings.Zoom          = 2.0
        camSettings.ParallelProjection = False
        camSettings.computePlaneNormal = False
        camSettings.ResetCameraBeforeZoom = True
        
        Predefined3DTethaPhiPVRenderView.__init__(self, '3D hiRes', camSettings, '3DhiRes', None)
        
        self.setThetaRange(thetaRange)
        self.setPhiRange(phiRange)    
        self.setSmallAxis(SmallAxis)
        self.setBigAxis(BigAxis)
        self.setImageScaleUp(3)
        
        return

##########################################################################################################################################################################################################

class GenericSurfacePart(object):
    """
    Generic PV surface source based on polygonal or unstructed grid
    """
    
    def __init__(self, name, inputDir='.', arraysToLoad = [], verbose = False):
        
        """
        Object constructor
        ~~~~~~~~~~~~~~~~~~
        @param : name         : String value containing a regular expression or starting string of the file name which is going to be looked for.
        @param : inputDir     : The name of the directory where to look for a file of type .vtu or .vtp
        @param : arraysToLoad : When defined, the object will try to load only the arrays defined within the list. By default, all available arrays will be loaded.
        @author : Frank Albina
        @organization : Sauber Motorsport AG
        """
        
        ### Set initial values of object here
        self.partName = ''                  ### Search string as regular expression
        self.partFiles = 'not-set'          ### Defines the part files associated to the search string
        self.symmetric = False              ### For detecting if the surface source is symmetric or not
        self.pointDataArrays = []           ### Point data arrays to be loaded for performing further postprocessing
        self.partReaders = None             ### 
        self.partSource = None              ###
        ### Set "private" variables
        self.__verbose__ = verbose          ### For producing verbose output
        
        
        ############################################################################
        #####                      FILE LIST COLLECTION                        #####
        ############################################################################                        
        
        ### Try to check for VTU or VTP files based on regular expression starting with input variable name
        ### VTP files have precedence over VTU files. 
        if (name):
            ### Collect all files from input directory which are ending with .vtp or .vtu 
            vtpFileNames = glob.glob(os.path.join(inputDir,str(name)+'*.vtp'))
            vtuFileNames = glob.glob(os.path.join(inputDir,str(name)+'*.vtu'))
            ### Collect all VTP files
            if (vtpFileNames):
                if (self.__verbose__): 
                    print '\t\t\t Found VTP files corresponding to regular file expression '+str(os.path.join(inputDir,str(name)+'*.vtp'))+'.'
                self.partName = name
                self.partFiles = []    
                self.partFiles.extend(vtpFileNames)
            ### Add all VTU files
            if (vtuFileNames):
                    if (self.__verbose__): 
                        print '\t\t\t Found VTU files corresponding to regular file expression '+str(os.path.join(inputDir,str(name)+'*.vtu'))+'.'
                    if (self.partFiles == 'not-set'): 
                        self.partFiles = []
                        self.partName = name    
                    self.partFiles.extend(vtuFileNames)
            ### Bail out if no files could be found
            if (self.partFiles == 'not-set'):
                if (self.__verbose__):
                    print '\t\t\t No file corresponding to regular file expression '+str(os.path.join(inputDir,str(name)+'*.vt[u,p]'))+' could be found.'
                return
        else:
            ### In case that no regular expression was found
            raise ValueError, 'iGenericSurfacePart::init: At least a name or a regular expression is required for initialising the object.'
        
        ############################################################################
        #####               SET POINT ARRAYS TO LOAD (IF REQUIRED)             #####
        ############################################################################
        
        ### Check the list of arrays to load for type and element type (to be performed only if the list is passed ).
        if (arraysToLoad):
            if (isinstance(arraysToLoad,list)):
                for item in arraysToLoad:
                    if (not isinstance(item,str)):
                        raise ValueError, 'iGenericSurfacePart::init: All elements of input parameter arraysToLoad needs to be of type str, but received an element of type '+str(type(item))+' instead.'
                self.pointDataArrays = arraysToLoad
            else:
                raise TypeError, 'iGenericSurfacePart::init: Expecting a list to be passed, but got an object of type '+str(type(arraysToLoad))+' instead.'
        
        return
    
    def ListProperties(self):
        """
        Prints a list of all object properties which can be set
        """
        propList = []
        for prop in self.__dict__:
            propList.append(str(prop))
        print propList
        return
    
    def SetReaders(self):
        """
        Method for getting the appropriate reader for the requested part.
        @return : None if the readers could not be set
        """
        self.partReaders = None
        
        if (self.partFiles != 'not-set'):
            
            ### Set container
            self.partReaders = []
            
            for file in self.partFiles:
                ### Define the reader which will load the part with name partName
                extension = os.path.splitext(file)[1]
                if (extension == '.vtp'):
                    reader = paraview.simple.XMLPolyDataReader(FileName=file)
                elif (extension == '.vtu'):
                    reader = paraview.simple.XMLUnstructuredGridReader(FileName=file)
                else:
                    raise ValueError, 'iGenericSurfacePart::SetReaders: Unsupported file type with extension '+str(extension)+'.'
                ### Update information
                reader.UpdatePipelineInformation()
                
                ### Get all arrays we need and check if they are available in the source file
                arrays = self.pointDataArrays
                self.pointDataArrays = []
                for tag in arrays:
                    if reader.PointData.has_key(tag):
                        self.pointDataArrays.append(tag)
                reader.PointArrayStatus = self.pointDataArrays
            
                self.partReaders.append(reader)
                
        return
    
    def SetSource(self):
        """
        Method for setting one source defined on all object readers. 
        """
        
        if (self.partReaders):
        
            isSymmetric = True
        
            #####################################################################
            ####       CHECK IF ALL READER FILES HAVE SYMMETRIC DATA         ####
            #####################################################################        
            for reader in self.partReaders:
             
                ### Read the data 
                reader.UpdatePipeline()
            
                ### Get for the model bounds.
                bounds = reader.GetDataInformation().GetBounds()
            
                ### Check if all bounds are the same. Should there be a mismatch, raise an error.
                if (bounds[3] < 0.01):
                    if (not isSymmetric):
                        raise ValueError, 'iGenericSurfacePart::SetSource: Detected symmetric and non-symmetric parts while reading surface files. This is not allowed.'
                else:
                    isSymmetric = False

                ### All parts are either symmetric or non-symmetric.
                self.symmetric = isSymmetric
                if (self.symmetric) and (self.__verbose__): print '\t\t\t   Part is symmetric and will be mirrored for rendering.'
            
            #####################################################################
            ####          GENERATE ONE PV SOURCE FROM ALL READERS            ####
            #####################################################################
            
            if (len(self.partReaders) > 1):
                readerSourceList = []   
                for reader in self.partReaders:
                    d3 = paraview.simple.D3(Input=reader,BoundaryMode=0, MinimalMemory=1)
                    if (self.symmetric):
                        reflector = paraview.simple.Reflect(Input=d3, Plane='Y', CopyInput=0)
                        readerSourceList.append( paraview.simple.AppendDatasets( Input=[d3, reflector] ) )
                    else:
                        readerSourceList.append(d3)
                self.partSource = paraview.simple.AppendDatasets(Input=readerSourceList)
            else:
                d3 = paraview.simple.D3(Input=reader,BoundaryMode=0, MinimalMemory=1)
                if (self.symmetric):
                    reflector = paraview.simple.Reflect(Input=d3, Plane='Y', CopyInput=0)
                    self.partSource = paraview.simple.AppendDatasets( Input=[d3, reflector] )
                else:
                    self.partSource = d3
            
        if (self.partSource):
            self.partSource.UpdatePipeline()
                       
        return
    
    def Update(self):
        self.SetReaders()
        self.SetSource()
        return

##########################################################################################################################################################################################################

def getCommandLineArguments():
    
    """
    Defines and processes the command line arguments.
    Required values are:
       --input-dir   : input directory from where one can find the VTP files to be rendered and containing the delta values calculated.
       --output-dir  : output directory where to put the rendered images (optional). If not set, then the rendering is performed to screen.
    """
    
    clParser = argparse.ArgumentParser(prog='renderBenchmarkSurfaceLIC.py', formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help='Python script for performing a PV surface LIC rendering.')
    ### Input directory
    clParser.add_argument('--input-dir', dest='inputDir', type=str, help='Input directory where the files to be processed are stored.', required=True)
    ### Output directory
    clParser.add_argument('--output-dir', dest='outputDir', type=str, help='Output directory where to store the generated images.', required=False)
    ### Set verbosity on
    clParser.add_argument('--verbose', dest='verbose', default=False, action='store_true', help='Sets verbosity on', required=False)
    ### Set the base image resolution
    clParser.add_argument('--baseResolution', dest='baseResolution', type=str, default='1600x800', help='Defines the standard screen resolution used for screen dumps. Needs to be YxZ.', required=False)
    
    args = clParser.parse_args()
    
    return args
    
def main():
     
    ### Set the stdout & stderr output to be unbuffered
    sys.stdout = Flushfile(sys.stdout)
    sys.stderr = Flushfile(sys.stdout) 
       
    cmlArgs = getCommandLineArguments()
    
    #paraview.simple.Connect('localhost',11111)
    
    print
    print "\t\t/============================================================\\"
    print '\t\t|=                  PARAVIEW RENDER BENCHMARK               =|'
    print '\t\t|=              Based on ParaView batch rendering           =|'
    print "\t\t|=                                                          =|"
    print "\t\t|=    This software is property of Sauber Motorsports AG    =|"
    print "\t\t|=    !!! Do not redistribute without owner's consent !!!   =|"
    print "\t\t\\============================================================/"
    print "\n" 
    
    ### Toggle progress printing off
    paraview.servermanager.ToggleProgressPrinting()
     
    ### Load the SurfaceLIC plugin
    paraview.servermanager.LoadPlugin("libSurfaceLIC.so") 
       
    varsToRender = [ 'velocity' ]
    
    startTime = time.time()
    print '\t\t Start gathering data for rendering...'
    toRenderDict = {}
    
    for part in KnownParts:
        
        toRenderCandidate = GenericSurfacePart(part, cmlArgs.inputDir, varsToRender, cmlArgs.verbose)
        toRenderCandidate.Update()
        
        if (toRenderCandidate.partReaders):
            toRenderDict[part] = toRenderCandidate
            print '\t\t\t Found data for part '+str(part)+' to be rendered ',
            if (toRenderDict[part].symmetric):
                print '(symmetric).'
            else:
                print '(full).'
            print '\t\t\t\tAvailable arrays: ',toRenderDict[part].pointDataArrays
            for reader in toRenderDict[part].partReaders:
                print '\t\t\t\tLoaded arrays %s from file %s' % (str(reader.PointArrayStatus), str(os.path.basename(os.path.realpath(reader.FileName[0]))))
        else:
            del toRenderCandidate
            
    ### Append all datasets into one for rendering
    sourcesList = []
    for key in toRenderDict:
        if  toRenderDict[key].partSource: sourcesList.append(toRenderDict[key].partSource)
    oneSource = paraview.simple.AppendDatasets(Input=sourcesList)
    oneSource.UpdatePipeline()
            
    print '\t\t Terminated gathering data in %.2f [s].' % (time.time() - startTime)
    
    #############################################################################################################
    ####               CREATE RENDERING VIEW AND DEFINE SURFACE REPRESENTATIONS                               ###
    #############################################################################################################
    
    ### Create the rendering view
    view = paraview.simple.CreateRenderView()
    
    baseResolution = cmlArgs.baseResolution.split('x')
    WIDTH = int(baseResolution[0])
    HEIGHT = int(baseResolution[1])
    view.ViewSize   = [ WIDTH, HEIGHT ]

    view.Background = [1, 1, 1]
   
    ### Define representation of surface from part source
    rep = paraview.simple.Show(oneSource, view)
    ### Define representation (surface with edges)
    rep.SetRepresentationType('Surface LIC')
    rep.SetPropertyWithName('LICIntensity',0.8)
    rep.SetPropertyWithName('StepSize',0.5)
    rep.SetPropertyWithName('EnhanceContrast', 'Off')
    rep.SetPropertyWithName('CompositeStrategy', 'AUTO')
    rep.SetPropertyWithName('EnhancedLIC',1)
    rep.SetPropertyWithName('NormalizeVectors',1)
    rep.SetPropertyWithName('MaskOnSurface',1)
    rep.SetPropertyWithName('NumberOfSteps',40)
    rep.SetPropertyWithName('MinNoiseValue', 0)
    rep.SetPropertyWithName('MaxNoiseValue', 0.8)
    rep.SetPropertyWithName('NoiseGrainSize', 2)
    rep.SetPropertyWithName('NoiseGeneratorSeed', 1)
    rep.SetPropertyWithName('NoiseTextureSize', 128)
    rep.SetPropertyWithName('NumberOfNoiseLevels', 1024)
    rep.SetPropertyWithName('NoiseType', 'Gaussian')
    rep.SetPropertyWithName('LowLICContrastEnhancementFactor',0.0)
    rep.SetPropertyWithName('HighLICContrastEnhancementFactor', 0.0)
    rep.SetPropertyWithName('AntiAlias',1)
    

    

    rep.InterpolateScalarsBeforeMapping = 0
    ### Define representation as cell or point data
    ### Set visibility on
    rep.Visibility = 1
    
    ### Define a 3D high resolution renderer
    r = Predefined3DHiResPVRenderView( range(0,360,30), range(-80,120,40), 12.0, 12.0)
    r.setImageScaleUp(1)
    
    #############################################################################################################
    ####                           PERFORM RENDERING AND STORE IMAGE FILES                                    ###
    #############################################################################################################
        
    ### Change the prefix for file dump
    r.setPrefix('oilflow-')

    ### Now generate the images
    print '\t\t Start rendering...'
    startTime = time.time()
    print '\t\t\tGenerating view '+str(r.name)+'...',
    if cmlArgs.outputDir is not None:
        r.generate(view, cmlArgs.outputDir)
    else:
        r.generate(view, None) 
    print 'Done in %.2f [s].' % (time.time()-startTime)
     
    return

if __name__ == '__main__':
    main()
