#============================================================================== # Copyright (c) 2015, Kitware Inc., Los Alamos National Laboratory # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, this # list of conditions and the following disclaimer in the documentation and/or other # materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors may # be used to endorse or promote products derived from this software without specific # prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. #============================================================================== import cinema_store import paraview import pv_explorers from itertools import imap import numpy as np def record_visibility(): proxies = [] view_info = {} view_proxy = paraview.simple.GetActiveView() view_info['proxy'] = "__view_info" view_info[ 'orientation_axis_visibility'] = view_proxy.OrientationAxesVisibility camera = view_proxy.GetActiveCamera() view_info['position'] = camera.GetPosition() view_info['view_up'] = camera.GetViewUp() view_info['focal_point'] = camera.GetFocalPoint() proxies.append(view_info) source_proxies = paraview.servermanager.ProxyManager().GetProxiesInGroup( "sources") for key in source_proxies: listElt = {} proxy = source_proxies[key] listElt['proxy'] = proxy listElt['visibility'] = None listElt['scalar_bar_visibility'] = False listElt['color_array_name'] = None listElt['color_array_association'] = None rep = paraview.simple.GetDisplayProperties(proxy) if rep != None: listElt['visibility'] = rep.Visibility listElt['scalar_bar_visibility'] = rep.IsScalarBarVisible(view_proxy) listElt['color_array_name'] = rep.ColorArrayName.GetArrayName() listElt['color_array_association'] = rep.ColorArrayName.GetAssociation() proxies.append(listElt) return proxies def restore_visibility(proxies): view_proxy = paraview.simple.GetActiveView() for listElt in proxies: if listElt['proxy'] == "__view_info": view_proxy.OrientationAxesVisibility = listElt[ 'orientation_axis_visibility'] camera = view_proxy.GetActiveCamera() camera.SetPosition(listElt['position']) camera.SetViewUp(listElt['view_up']) camera.SetFocalPoint(listElt['focal_point']) else: proxy = listElt['proxy'] vis = listElt['visibility'] if vis != None: rep = paraview.simple.GetDisplayProperties(proxy) if rep != None: rep.Visibility = listElt['visibility'] if listElt['color_array_association']: rep.SetScalarColoring( listElt['color_array_name'], paraview.servermanager.GetAssociationFromString( listElt['color_array_association'])) if listElt['scalar_bar_visibility']: rep.SetScalarBarVisibility(view_proxy, listElt['scalar_bar_visibility']) def inspect(skip_invisible=True): """ Produces a representation of the pipeline that is easier to work with. Thanks Scott Wittenburg and the pv mailing list for this gem """ source_proxies = paraview.servermanager.ProxyManager().GetProxiesInGroup("sources") proxies = [] proxybyId = {} for key in source_proxies: listElt = {} listElt['name'] = key[0] listElt['id'] = key[1] proxy = source_proxies[key] #skip the invisible rep = paraview.simple.GetDisplayProperties(proxy) if skip_invisible: if rep == None: #for example, writers in catalyst pipeline #todo: is it possible for these to have decendents that are visible? continue listElt['visibility'] = 0 if (rep == None) else rep.Visibility parentId = '0' try: if hasattr(proxy, 'Input'): parentId = proxy.Input.GetGlobalIDAsString() except AttributeError: parentId = '0' listElt['parent'] = parentId proxies.append(listElt) proxybyId[key[1]] = listElt if skip_invisible: #reparent upward over invisible parents for l in proxies: pid = l['parent'] while pid != '0' and proxybyId[pid]['visibility'] == 0: pid = proxybyId[pid]['parent'] l['parent'] = pid #remove invisible proxies themselves pxies = [] for l in proxies: if l['visibility'] != 0: pxies.append(l) else: pxies = proxies return pxies def get_pipeline(): proxies = inspect(skip_invisible=False) for proxy in proxies: source = paraview.simple.FindSource(proxy['name']) numberOfProducers = source.GetNumberOfProducers() if proxy['parent'] is '0' and numberOfProducers > 0: # this proxy is the result of a merge parents = [] for i in xrange(numberOfProducers): parents.append(source.GetProducerProxy(i).GetGlobalIDAsString()) proxy['parents'] = parents else: proxy['parents'] = [proxy['parent']] del proxy['parent'] for proxy in proxies: proxy['children'] = [p['id'] for p in proxies if proxy['id'] in p['parents']] return proxies def float_limiter(x): #a shame, but needed to make sure python, java and (directory/file)name agree if isinstance(x, (float)): #return '%6f' % x #arbitrarily chose 6 decimal places return '%.6e' % x #arbitrarily chose 6 significant digits else: return x # Keeps a link between a filter and its explorer-track. Populated in addFilterValue() # and queried in explore() explorerDir = {} def add_filter_value(name, cs, userDefinedValues): source = paraview.simple.FindSource(name) # plane offset generator (for Slice or Clip) def generate_offset_values(): bounds = source.Input.GetDataInformation().DataInformation.GetBounds() minPoint = np.array([bounds[0], bounds[2], bounds[4]]) maxPoint = np.array([bounds[1], bounds[3], bounds[5]]) scaleVec = maxPoint - minPoint # adjust offset size depending on the plane orientation if hasattr(source, 'SliceType'): n = source.SliceType.Normal elif hasattr(source, 'ClipType'): n = source.ClipType.Normal sNormal = np.array([n[0] * scaleVec[0], n[1] * scaleVec[1], n[2] * scaleVec[2]]) steps = 3 # generate N slice offsets offsetStep = np.linalg.norm(sNormal) / steps values = np.arange(-(steps/2), steps/2) * offsetStep return values.tolist() # generate values depending on the type of filter if isinstance(source, paraview.simple.servermanager.filters.Clip): # grab values from ui or generate defaults values = userDefinedValues[name] if (name in userDefinedValues) else generate_offset_values() if len(values) == 0: values = generate_offset_values() # add sublayer and create the appropriate track cs.add_control(name, cinema_store.make_parameter(name, values, typechoice='hidden')) explorerDir[name] = pv_explorers.Clip(name, source) elif isinstance(source, paraview.simple.servermanager.filters.Slice): # grab values from ui or generate defaults values = userDefinedValues[name] if (name in userDefinedValues) else generate_offset_values() if len(values) == 0: values = generate_offset_values() # add sublayer and create the appropriate track cs.add_control(name, cinema_store.make_parameter(name, values, typechoice='hidden')) explorerDir[name] = pv_explorers.Slice(name, source) elif isinstance(source, paraview.simple.servermanager.filters.Contour): def generate_contour_values(): # grab values from ui or generate defaults vRange = source.Input.GetDataInformation().DataInformation.GetPointDataInformation().GetArrayInformation(0).GetComponentRange(0) return np.linspace(vRange[0], vRange[1], 5).tolist() # generate 5 contour values values = userDefinedValues[name] if (name in userDefinedValues) else generate_contour_values() if len(values) == 0: values = generate_contour_values() # add sublayer and create the appropriate track cs.add_control(name, cinema_store.make_parameter(name, values, typechoice='hidden')) explorerDir[name] = pv_explorers.Contour(name, source) def filter_has_parameters(name): source = paraview.simple.FindSource(name) return any(imap(lambda filter: isinstance(source, filter), [paraview.simple.servermanager.filters.Clip, paraview.simple.servermanager.filters.Slice, paraview.simple.servermanager.filters.Contour])) def add_control_and_colors(name, cs): source = paraview.simple.FindSource(name) #make up list of color options fields = {'depth':'depth','luminance':'luminance'} ranges = {} defaultName = None view_proxy = paraview.simple.GetActiveView() rep = paraview.simple.GetRepresentation(source, view_proxy) if rep.Representation != 'Outline': cda = source.GetCellDataInformation() for a in range(0, cda.GetNumberOfArrays()): arr = cda.GetArray(a) arrName = arr.GetName() if not arrName == "Normals": for i in range(0, arr.GetNumberOfComponents()): fName = arrName+"_"+str(i) fields[fName] = 'value' ranges[fName] = arr.GetRange(i) if defaultName == None: defaultName = fName pda = source.GetPointDataInformation() for a in range(0, pda.GetNumberOfArrays()): arr = pda.GetArray(a) arrName = arr.GetName() if not arrName == "Normals": for i in range(0, arr.GetNumberOfComponents()): fName = arrName+"_"+str(i) fields[fName] = 'value' ranges[fName] = arr.GetRange(i) if defaultName == None: defaultName = fName if defaultName == None: fields['white']='rgb' defaultName='white' cparam = cinema_store.make_field("color"+name, fields, default=defaultName, valueRanges=ranges) cs.add_field("color"+name,cparam,'vis',[name]) def make_cinema_store(proxies, ocsfname, forcetime=False, _userDefinedValues={}): """ Takes in the pipeline, structured as a tree, and makes a cinema store definition containing all the parameters we might will vary. """ if "phi" in _userDefinedValues: phis = _userDefinedValues["phi"] else: #phis = [0,45,90,135,180,225,270,315,360] phis = [0,180,360] if "theta" in _userDefinedValues: thetas = _userDefinedValues["theta"] else: #thetas = [0,20,40,60,80,100,120,140,160,180] thetas = [0,90,180] tvalues = [] cs = cinema_store.FileStore(ocsfname) try: cs.load() tprop = cs.get_parameter('time') tvalues = tprop['values'] #start with clean slate, other than time cs = cinema_store.FileStore(ocsfname) except IOError, KeyError: pass cs.add_metadata({'type':'composite-image-stack'}) cs.add_metadata({'store_type':'FS'}) cs.add_metadata({'version':'0.0'}) pipeline = get_pipeline() cs.add_metadata({'pipeline':pipeline}) vis = [proxy['name'] for proxy in proxies] cs.add_layer("vis",cinema_store.make_parameter('vis', vis)) for proxy in proxies: proxy_name = proxy['name'] add_filter_value(proxy_name,cs,_userDefinedValues) dependency_set = set([proxy['id']]) repeat = True while repeat: repeat = False deps = set(proxy['id'] for proxy in proxies if proxy['parent'] in dependency_set) if deps - dependency_set: dependency_set = dependency_set.union(deps) repeat = True dependency_list = [proxy['name'] for proxy in proxies if proxy['id'] in dependency_set] cs.assign_parameter_dependence(proxy_name,'vis',dependency_list) add_control_and_colors(proxy_name,cs) cs.assign_parameter_dependence("color"+proxy_name,'vis',[proxy_name]) fnp = "" if forcetime: #time specified, use it, being careful to append if already a list tvalues.append(forcetime) tprop = cinema_store.make_parameter('time', tvalues) cs.add_parameter('time', tprop) fnp = fnp+"{time}_" else: #time not specified, try and make them automatically times = paraview.simple.GetAnimationScene().TimeKeeper.TimestepValues if not times: pass else: prettytimes = [float_limiter(t) for t in times] cs.add_parameter("time", cinema_store.make_parameter('time', prettytimes)) fnp = fnp+"{time}_" cs.add_parameter("phi", cinema_store.make_parameter('phi', phis)) cs.add_parameter("theta", cinema_store.make_parameter('theta', thetas)) fnp = fnp+"{phi}_{theta}.png" cs.filename_pattern = fnp return cs def testexplore(cs): """ For debugging, takes in the cinema store and prints out everything that we'll take snapshots off """ import explorers import copy class printer(explorers.Explorer): def execute(self, desc): p = copy.deepcopy(desc) x = 'phi' if x in p.keys(): print x, ":", desc[x], ",", del p[x] x = 'theta' if x in p.keys(): print x, ":", desc[x], ",", del p[x] for x in sorted(p.keys()): print x, ":", p[x], ",", print params = cs.parameter_list.keys() e = printer(cs, params, []) e.explore() def explore(cs, proxies, iSave=True, currentTime=None): """ Takes in the store, which contains only the list of parameters, """ # import pv_explorers import explorers view_proxy = paraview.simple.GetActiveView() dist = paraview.simple.GetActiveCamera().GetDistance() #associate control points wlth parameters of the data store cam = pv_explorers.Camera([0,0,0], [0,1,0], dist, view_proxy) params = cs.parameter_list.keys() tracks = [] tracks.append(cam) cols = [] ctime_float=None if currentTime: ctime_float = float(currentTime['time']) #hide all annotations view_proxy.OrientationAxesVisibility = 0 for x in proxies: name = x['name'] for y in params: if (y in explorerDir) and (name == y): #print "name in ExplorerDir: ", y, ", ", explorerDir[y] tracks.append(explorerDir[y]) if name in y: #print "N", name #print "X", x #print "Y", y #visibility of the layer sp = paraview.simple.FindSource(name) rep = paraview.simple.GetRepresentation(sp, view_proxy) #hide all annotations if rep.LookupTable: rep.SetScalarBarVisibility(view_proxy, False) tc1 = pv_explorers.SourceProxyInLayer(name, rep) lt = explorers.Layer('vis', [tc1]) tracks.append(lt) #fields for the layer cC = pv_explorers.ColorList() cC.AddDepth('depth') cC.AddLuminance('luminance') sp.UpdatePipeline(ctime_float) cda = sp.GetCellDataInformation() numVals = 0 if rep.Representation != 'Outline': for a in range(0, cda.GetNumberOfArrays()): arr = cda.GetArray(a) arrName = arr.GetName() if not arrName == "Normals": for i in range(0,arr.GetNumberOfComponents()): numVals+=1 cC.AddValueRender(arrName+"_"+str(i), True, arrName, i, arr.GetRange(i)) pda = sp.GetPointDataInformation() for a in range(0, pda.GetNumberOfArrays()): arr = pda.GetArray(a) arrName = arr.GetName() if not arrName == "Normals": for i in range(0,arr.GetNumberOfComponents()): numVals+=1 cC.AddValueRender(arrName+"_"+str(i), False, arrName, i, arr.GetRange(i)) if numVals == 0: cC.AddSolidColor('white', [1,1,1]) col = pv_explorers.Color("color"+name, cC, rep) tracks.append(col) cols.append(col) e = pv_explorers.ImageExplorer(cs, params, tracks, view_proxy, iSave) for c in cols: c.imageExplorer = e times = paraview.simple.GetAnimationScene().TimeKeeper.TimestepValues if not times: e.explore(currentTime) else: for t in times: view_proxy.ViewTime=t e.explore({'time':float_limiter(t)}) def record(csname="/tmp/test_pv/info.json"): paraview.simple.Render() view = paraview.simple.GetActiveView() camera = view.GetActiveCamera() pxystate = record_visibility() view.LockBounds = 1 p = inspect() cs = make_cinema_store(p, csname) #if test: # testexplore(cs) #else: explore(cs, p) view.LockBounds = 0 restore_visibility(pxystate) cs.save() def export_scene(baseDirName, viewSelection, trackSelection): '''This explores a set of user-defined views and tracks. export_scene is called from vtkCinemaExport. The expected order of parameters is as follows: - viewSelection (following the format defined in Wrapping/Python/paraview/cpstate.py): Directory of the form {'ViewName' : [parameters], ...}, with parameters defined in the order: Image filename, freq, fittoscreen, magnification, width, height, cinema). - trackSelection: Directory of the form {'TrackName' : [v1, v2, v3], ...} Note: baseDirName is used as the parent directory of the database generated for each view in viewSelection. 'Image filename' is used as the database directory name. ''' import paraview.simple as pvs # save initial state initialView = pvs.GetActiveView() pvstate = record_visibility() atLeastOneViewExported = False for viewName, viewParams in viewSelection.iteritems(): # check if this view was selected to export as spec b cinemaParams = viewParams[6] if len(cinemaParams) == 0: print "Skipping view: Not selected to export as cinema spherical." continue # get the view and save the initial status view = pvs.FindView(viewName) pvs.SetActiveView(view) view.ViewSize = [viewParams[4], viewParams[5]] pvs.Render() # fully renders the scene (if not, some faces might be culled) view.LockBounds = 1 #writeFreq = viewParams[1] # TODO where to get the timestamp in this case? #if (writeFreq and timestamp % writeFreq == 0): #magnification = viewParams[3] # Not used in cinema (TODO hide in UI) fitToScreen = viewParams[2] if fitToScreen != 0: if view.IsA("vtkSMRenderViewProxy") == True: view.ResetCamera() elif view.IsA("vtkSMContextViewProxy") == True: view.ResetDisplay() else: print ' do not know what to do with a ', view.GetClassName() userDefValues = {} if "theta" in cinemaParams: userDefValues["theta"] = cinemaParams["theta"] if "phi" in cinemaParams: userDefValues["phi"] = cinemaParams["phi"] userDefValues.update(trackSelection) # generate file path import os.path viewFileName = viewParams[0] viewDirName = viewFileName[0:viewFileName.rfind("_")] #strip _num.ext filePath = os.path.join(baseDirName, viewDirName, "info.json") p = inspect() cs = make_cinema_store(p, filePath, forcetime = False, _userDefinedValues = userDefValues) explore(cs, p) view.LockBounds = 0 cs.save() atLeastOneViewExported = True if not atLeastOneViewExported: print "No view was selected to export as cinema spherical." return # restore initial state pvs.SetActiveView(initialView) restore_visibility(pvstate)