def booleanOperation(actor1, actor2, operation='plus', c=None, alpha=1, wire=False, bc=None, edges=False, legend=None, texture=None): '''Volumetric union, intersection and subtraction of surfaces [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/boolean.py) ''' try: bf = vtk.vtkBooleanOperationPolyDataFilter() except AttributeError: vc.printc('Boolean operation only possible for vtk version >= 8', c='r') return None poly1 = vu.polydata(actor1, True) poly2 = vu.polydata(actor2, True) if operation.lower() == 'plus': bf.SetOperationToUnion() elif operation.lower() == 'intersect': bf.SetOperationToIntersection() elif operation.lower() == 'minus': bf.SetOperationToDifference() bf.ReorientDifferenceCellsOn() if vu.vtkMV: bf.SetInputData(0, poly1) bf.SetInputData(1, poly2) else: bf.SetInputConnection(0, poly1.GetProducerPort()) bf.SetInputConnection(1, poly2.GetProducerPort()) bf.Update() actor = vu.makeActor(bf.GetOutput(), c, alpha, wire, bc, edges, legend, texture) return actor
def align(source, target, iters=100, rigid=False, legend=None): ''' Return a copy of source actor which is aligned to target actor through vtkIterativeClosestPointTransform() method. The core of the algorithm is to match each vertex in one surface with the closest surface point on the other, then apply the transformation that modify one surface to best match the other (in the least-square sense). [**Example1**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/align1.py) [**Example2**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/align2.py) ''' source = vu.polydata(source) target = vu.polydata(target) icp = vtk.vtkIterativeClosestPointTransform() icp.SetSource(source) icp.SetTarget(target) icp.SetMaximumNumberOfIterations(iters) if rigid: icp.GetLandmarkTransform().SetModeToRigidBody() icp.StartByMatchingCentroidsOn() icp.Update() icpTransformFilter = vtk.vtkTransformPolyDataFilter() vu.setInput(icpTransformFilter, source) icpTransformFilter.SetTransform(icp) icpTransformFilter.Update() poly = icpTransformFilter.GetOutput() actor = vu.makeActor(poly, legend=legend) if hasattr(source, 'GetProperty'): actor.SetProperty(source.GetProperty()) setattr(actor, 'transform', icp.GetLandmarkTransform()) return actor
def write(obj, fileoutput): ''' Write 3D object to file. Possile extensions are: .vtk, .ply, .obj, .stl, .byu, .vtp, .xyz, .tif ''' fr = fileoutput.lower() if '.vtk' in fr: w = vtk.vtkPolyDataWriter() elif '.ply' in fr: w = vtk.vtkPLYWriter() elif '.stl' in fr: w = vtk.vtkSTLWriter() elif '.vtp' in fr: w = vtk.vtkXMLPolyDataWriter() elif '.xyz' in fr: w = vtk.vtkSimplePointsWriter() elif '.byu' in fr or fr.endswith('.g'): w = vtk.vtkBYUWriter() elif '.obj' in fr: obj = vu.polydata(obj, True) w = vtk.vtkOBJExporter() w.SetFilePrefix(fileoutput.replace('.obj', '')) vc.printc('Please use write(vp.renderWin)', c=3) w.SetInput(obj) w.Update() vc.printc("Saved file: " + fileoutput, c='g') return elif '.tif' in fr: wr = vtk.vtkTIFFWriter() wr.SetFileDimensionality(len(obj.GetDimensions())) vu.setInput(wr, obj) wr.SetFileName(fileoutput) wr.Write() vc.printc("TIFF stack saved as: " + fileoutput, c='g') return else: vc.printc('Unavailable format in file ' + fileoutput, c='r') exit(1) try: obj = vu.polydata(obj, True) vu.setInput(w, vu.polydata(obj, True)) w.SetFileName(fileoutput) w.Write() vc.printc("Saved file: " + fileoutput, c='g') except: vc.printc("Error saving: " + fileoutput, c='r')
def curvature(actor, method=1, r=1, alpha=1, lut=None, legend=None): ''' Build a copy of vtkActor that contains the color coded surface curvature following four different ways to calculate it: method = 0-gaussian, 1-mean, 2-max, 3-min [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/tutorial.py) ''' poly = vu.polydata(actor) cleaner = vtk.vtkCleanPolyData() vu.setInput(cleaner, poly) curve = vtk.vtkCurvatures() curve.SetInputConnection(cleaner.GetOutputPort()) curve.SetCurvatureType(method) curve.InvertMeanCurvatureOn() curve.Update() print('CurvatureType set to:', method) if not lut: lut = vtk.vtkLookupTable() lut.SetNumberOfColors(256) lut.SetHueRange(0.15, 1) lut.SetSaturationRange(1, 1) lut.SetValueRange(1, 1) lut.SetAlphaRange(alpha, 1) b = poly.GetBounds() sc = max([b[1]-b[0], b[3]-b[2], b[5]-b[4]]) lut.SetRange(-0.01/sc*r, 0.01/sc*r) cmapper = vtk.vtkPolyDataMapper() cmapper.SetInputConnection(curve.GetOutputPort()) cmapper.SetLookupTable(lut) cmapper.SetUseLookupTableScalarRange(1) cactor = vtk.vtkActor() cactor.SetMapper(cmapper) return cactor
def removeOutliers(points, radius, c='k', alpha=1, legend=None): ''' Remove outliers from a cloud of points within radius search ''' isactor = False if isinstance(points, vtk.vtkActor): isactor = True poly = vu.polydata(points) else: src = vtk.vtkPointSource() src.SetNumberOfPoints(len(points)) src.Update() vpts = src.GetOutput().GetPoints() for i, p in enumerate(points): vpts.SetPoint(i, p) poly = src.GetOutput() removal = vtk.vtkRadiusOutlierRemoval() vu.setInput(removal, poly) removal.SetRadius(radius) removal.SetNumberOfNeighbors(5) removal.GenerateOutliersOff() removal.Update() rpoly = removal.GetOutput() print("# of removed outlier points: ", removal.GetNumberOfPointsRemoved(), '/', poly.GetNumberOfPoints()) outpts = [] for i in range(rpoly.GetNumberOfPoints()): outpts.append(list(rpoly.GetPoint(i))) outpts = np.array(outpts) if not isactor: return outpts actor = vs.points(outpts, c=c, alpha=alpha, legend=legend) return actor # return same obj for concatenation
def write(obj, fileoutput): ''' Write 3D object to file. Possile extensions are: .vtk, .ply, .obj, .stl, .byu, .vtp ''' fr = fileoutput.lower() if '.vtk' in fr: w = vtk.vtkPolyDataWriter() elif '.ply' in fr: w = vtk.vtkPLYWriter() elif '.obj' in fr: w = vtk.vtkOBJExporter() w.SetFilePrefix(fileoutput.replace('.obj', '')) vc.printc('Please use write(vp.renderWin)', 3) w.SetInput(obj) w.Update() vc.printc("Saved file: " + fileoutput, 'g') return elif '.stl' in fr: w = vtk.vtkSTLWriter() elif '.byu' in fr or '.g' in fr: w = vtk.vtkBYUWriter() elif '.vtp' in fr: w = vtk.vtkXMLPolyDataWriter() else: vc.printc('Unavailable format in file ' + fileoutput, c='r') exit(1) try: vu.setInput(w, vu.polydata(obj, True)) w.SetFileName(fileoutput) w.Write() vc.printc("Saved file: " + fileoutput, 'g') except: vc.printc("Error saving: " + fileoutput, 'r')
def smoothMLS1D(actor, f=0.2, showNLines=0): ''' Smooth actor or points with a Moving Least Squares variant. The list actor.variances contain the residue calculated for each point. Input actor's polydata is modified. f, smoothing factor - typical range s [0,2] showNLines, build an actor showing the fitting line for N random points ''' coords = vu.coordinates(actor) ncoords = len(coords) Ncp = int(ncoords * f / 10) nshow = int(ncoords) if showNLines: ndiv = int(nshow / showNLines) if Ncp < 3: vc.printc('Please choose a higher fraction than ' + str(f), 1) Ncp = 3 poly = vu.polydata(actor, True) vpts = poly.GetPoints() locator = vtk.vtkPointLocator() locator.SetDataSet(poly) locator.BuildLocator() vtklist = vtk.vtkIdList() variances, newline, acts = [], [], [] for i, p in enumerate(coords): locator.FindClosestNPoints(Ncp, p, vtklist) points = [] for j in range(vtklist.GetNumberOfIds()): trgp = [0, 0, 0] vpts.GetPoint(vtklist.GetId(j), trgp) points.append(trgp) if len(points) < 2: continue points = np.array(points) pointsmean = points.mean(axis=0) # plane center uu, dd, vv = np.linalg.svd(points - pointsmean) newp = np.dot(p - pointsmean, vv[0]) * vv[0] + pointsmean variances.append(dd[1] + dd[2]) newline.append(newp) if showNLines and not i % ndiv: fline = fitLine(points, lw=4, alpha=1) # fitting plane iapts = vs.points(points) # blue points acts += [fline, iapts] for i in range(ncoords): vpts.SetPoint(i, newline[i]) if showNLines: apts = vs.points(newline, c='r 0.6', r=2) ass = vu.makeAssembly([apts] + acts) return ass #NB: a demo actor is returned setattr(actor, 'variances', np.array(variances)) return actor #NB: original actor is modified
def surfaceIntersection(actor1, actor2, tol=1e-06, lw=3, c=None, alpha=1, legend=None): '''Intersect 2 surfaces and return a line actor''' try: bf = vtk.vtkIntersectionPolyDataFilter() except AttributeError: vc.printc('surfaceIntersection only possible for vtk version > 6','r') return None poly1 = vu.polydata(actor1, True) poly2 = vu.polydata(actor2, True) bf.SetInputData(0, poly1) bf.SetInputData(1, poly2) bf.Update() if c is None: c = actor1.GetProperty().GetColor() actor = vu.makeActor(bf.GetOutput(), c, alpha, 0, legend=legend) actor.GetProperty().SetLineWidth(lw) return actor
def surfaceIntersection(actor1, actor2, tol=1e-06, lw=3, c=None, alpha=1, legend=None): '''Intersect 2 surfaces and return a line actor. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/surfIntersect.py) ''' try: bf = vtk.vtkIntersectionPolyDataFilter() except AttributeError: vc.printc('surfaceIntersection only possible for vtk version > 6', c='r') return None poly1 = vu.polydata(actor1, True) poly2 = vu.polydata(actor2, True) bf.SetInputData(0, poly1) bf.SetInputData(1, poly2) bf.Update() if c is None: c = actor1.GetProperty().GetColor() actor = vu.makeActor(bf.GetOutput(), c, alpha, 0, legend=legend) actor.GetProperty().SetLineWidth(lw) return actor
def boundaries(actor, c='p', lw=5, legend=None): '''Build a copy of actor that shows the boundary lines of its surface.''' fe = vtk.vtkFeatureEdges() vu.setInput(fe, vu.polydata(actor)) fe.BoundaryEdgesOn() fe.FeatureEdgesOn() fe.ManifoldEdgesOn() fe.NonManifoldEdgesOn() fe.ColoringOff() fe.Update() bactor = vu.makeActor(fe.GetOutput(), c=c, alpha=1, legend=legend) bactor.GetProperty().SetLineWidth(lw) return bactor
def cluster(points, radius, legend=None): ''' Clustering of points in space. radius, is the radius of local search. Individual subsets can be accessed through actor.clusters [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/clustering.py) ![cluster](https://user-images.githubusercontent.com/32848391/46817286-2039ce00-cd7f-11e8-8b29-42925e03c974.png) ''' if isinstance(points, vtk.vtkActor): poly = vu.polydata(points) else: src = vtk.vtkPointSource() src.SetNumberOfPoints(len(points)) src.Update() vpts = src.GetOutput().GetPoints() for i, p in enumerate(points): vpts.SetPoint(i, p) poly = src.GetOutput() cluster = vtk.vtkEuclideanClusterExtraction() vu.setInput(cluster, poly) cluster.SetExtractionModeToAllClusters() cluster.SetRadius(radius) cluster.ColorClustersOn() cluster.Update() idsarr = cluster.GetOutput().GetPointData().GetArray('ClusterId') Nc = cluster.GetNumberOfExtractedClusters() sets = [[] for i in range(Nc)] for i, p in enumerate(points): sets[idsarr.GetValue(i)].append(p) acts = [] for i, aset in enumerate(sets): acts.append(vs.points(aset, c=i)) actor = vu.makeAssembly(acts, legend=legend) setattr(actor, 'clusters', sets) print('Nr. of extracted clusters', Nc) if Nc > 10: print('First ten:') for i in range(Nc): if i > 9: print('...') break print('Cluster #'+str(i)+', N =', len(sets[i])) print('Access individual clusters through attribute: actor.cluster') return actor
def align(source, target, iters=100, legend=None): ''' Return a copy of source actor which is aligned to target actor through vtkIterativeClosestPointTransform() method. ''' sprop = source.GetProperty() source = vu.polydata(source) target = vu.polydata(target) icp = vtk.vtkIterativeClosestPointTransform() icp.SetSource(source) icp.SetTarget(target) icp.SetMaximumNumberOfIterations(iters) icp.StartByMatchingCentroidsOn() icp.Update() icpTransformFilter = vtk.vtkTransformPolyDataFilter() vu.setInput(icpTransformFilter, source) icpTransformFilter.SetTransform(icp) icpTransformFilter.Update() poly = icpTransformFilter.GetOutput() actor = vu.makeActor(poly, legend=legend) actor.SetProperty(sprop) setattr(actor, 'transform', icp.GetLandmarkTransform()) return actor
def boundaries(actor, c='p', lw=5, legend=None): '''Build a copy of actor that shows the boundary lines of its surface. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/tutorial.py) ''' fe = vtk.vtkFeatureEdges() vu.setInput(fe, vu.polydata(actor)) fe.BoundaryEdgesOn() fe.FeatureEdgesOn() fe.ManifoldEdgesOn() fe.NonManifoldEdgesOn() fe.ColoringOff() fe.Update() bactor = vu.makeActor(fe.GetOutput(), c=c, alpha=1, legend=legend) bactor.GetProperty().SetLineWidth(lw) return bactor
def cluster(points, radius, legend=None): ''' Clustering of points in space. radius, is the radius of local search. Individual subsets can be accessed through actor.clusters ''' if isinstance(points, vtk.vtkActor): poly = vu.polydata(points) else: src = vtk.vtkPointSource() src.SetNumberOfPoints(len(points)) src.Update() vpts = src.GetOutput().GetPoints() for i, p in enumerate(points): vpts.SetPoint(i, p) poly = src.GetOutput() cluster = vtk.vtkEuclideanClusterExtraction() vu.setInput(cluster, poly) cluster.SetExtractionModeToAllClusters() cluster.SetRadius(radius) cluster.ColorClustersOn() cluster.Update() idsarr = cluster.GetOutput().GetPointData().GetArray('ClusterId') Nc = cluster.GetNumberOfExtractedClusters() sets = [[] for i in range(Nc)] for i, p in enumerate(points): sets[idsarr.GetValue(i)].append(p) acts = [] for i, aset in enumerate(sets): acts.append(vs.points(aset, c=i)) actor = vu.makeAssembly(acts, legend=legend) setattr(actor, 'clusters', sets) print('Nr. of extracted clusters', Nc) if Nc > 10: print('First ten:') for i in range(Nc): if i > 9: print('...') break print('Cluster #' + str(i) + ', N =', len(sets[i])) print('Access individual clusters through attribute: actor.cluster') return actor
def helix(startPoint=[0, 0, 0], endPoint=[1, 1, 1], coils=20, r=None, thickness=None, c='grey', alpha=1, legend=None, texture=None): ''' Build a spring actor of specified nr of coils between startPoint and endPoint ''' diff = endPoint - np.array(startPoint) length = np.linalg.norm(diff) if not length: return None if not r: r = length / 20 trange = np.linspace(0, length, num=50 * coils) om = 6.283 * (coils - .5) / length pts = [[r * np.cos(om * t), r * np.sin(om * t), t] for t in trange] pts = [[0, 0, 0]] + pts + [[0, 0, length]] diff = diff / length theta = np.arccos(diff[2]) phi = np.arctan2(diff[1], diff[0]) sp = vu.polydata(line(pts), False) t = vtk.vtkTransform() t.RotateZ(phi * 57.3) t.RotateY(theta * 57.3) tf = vtk.vtkTransformPolyDataFilter() vu.setInput(tf, sp) tf.SetTransform(t) tf.Update() tuf = vtk.vtkTubeFilter() tuf.SetNumberOfSides(12) tuf.CappingOn() vu.setInput(tuf, tf.GetOutput()) if not thickness: thickness = r / 10 tuf.SetRadius(thickness) tuf.Update() poly = tuf.GetOutput() actor = vu.makeActor(poly, c, alpha, legend=legend, texture=texture) actor.GetProperty().SetInterpolationToPhong() actor.SetPosition(startPoint) setattr(actor, 'base', np.array(startPoint)) setattr(actor, 'top', np.array(endPoint)) return actor
def extractLargestRegion(actor, legend=None): '''Keep only the largest connected part of a mesh and discard all the smaller pieces. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/largestregion.py) ''' conn = vtk.vtkConnectivityFilter() conn.SetExtractionModeToLargestRegion() conn.ScalarConnectivityOff() poly = vu.polydata(actor, True) vu.setInput(conn, poly) conn.Update() epoly = conn.GetOutput() if legend is True and hasattr(actor, 'legend'): legend = actor.legend eact = vu.makeActor(epoly, legend) pr = vtk.vtkProperty() pr.DeepCopy(actor.GetProperty()) eact.SetProperty(pr) return eact
def normals(actor, ratio=5, c=(0.6, 0.6, 0.6), alpha=0.8, legend=None): ''' Build a vtkActor made of the normals at vertices shown as arrows [**Example1**](https://github.com/marcomusy/vtkplotter/blob/master/examples/tutorial.py) [**Example2**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/fatlimb.py) ''' maskPts = vtk.vtkMaskPoints() maskPts.SetOnRatio(ratio) maskPts.RandomModeOff() src = vu.polydata(actor) vu.setInput(maskPts, src) arrow = vtk.vtkArrowSource() arrow.SetTipRadius(0.075) glyph = vtk.vtkGlyph3D() glyph.SetSourceConnection(arrow.GetOutputPort()) glyph.SetInputConnection(maskPts.GetOutputPort()) glyph.SetVectorModeToUseNormal() b = src.GetBounds() sc = max([b[1]-b[0], b[3]-b[2], b[5]-b[4]])/20. glyph.SetScaleFactor(sc) glyph.SetColorModeToColorByVector() glyph.SetScaleModeToScaleByVector() glyph.OrientOn() glyph.Update() glyphMapper = vtk.vtkPolyDataMapper() glyphMapper.SetInputConnection(glyph.GetOutputPort()) glyphMapper.SetScalarModeToUsePointFieldData() glyphMapper.SetColorModeToMapScalars() glyphMapper.ScalarVisibilityOn() glyphMapper.SelectColorArray("Elevation") glyphActor = vtk.vtkActor() glyphActor.SetMapper(glyphMapper) glyphActor.GetProperty().EdgeVisibilityOff() glyphActor.GetProperty().SetColor(vc.getColor(c)) # check if color string contains a float, in this case ignore alpha al = vc.getAlpha(c) if al: alpha = al glyphActor.GetProperty().SetOpacity(alpha) aactor = vu.makeAssembly([actor, glyphActor], legend=legend) return aactor
def extractLines(actor, n=5): coords = vu.coordinates(actor) poly = vu.polydata(actor, True) vpts = poly.GetPoints() locator = vtk.vtkPointLocator() locator.SetDataSet(poly) locator.BuildLocator() vtklist = vtk.vtkIdList() spts = [] for i, p in enumerate(coords): locator.FindClosestNPoints(n, p, vtklist) points = [] for j in range(vtklist.GetNumberOfIds()): trgp = [0, 0, 0] vpts.GetPoint(vtklist.GetId(j), trgp) if (p - trgp).any(): points.append(trgp) p0 = points.pop() - p dots = [np.dot(p0, ps - p) for ps in points] if len(np.unique(np.sign(dots))) == 1: spts.append(p) return np.array(spts)
def smoothLaplacian(actor, niter=15, relaxfact=0.1, edgeAngle=15, featureAngle=60): ''' Adjust mesh point positions using Laplacian smoothing. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/mesh_smoothers.py) ''' poly = vu.polydata(actor) cl = vtk.vtkCleanPolyData() vu.setInput(cl, poly) cl.Update() poly = cl.GetOutput() # removes the boudaries duplication smoothFilter = vtk.vtkSmoothPolyDataFilter() smoothFilter.SetInputData(poly) smoothFilter.SetNumberOfIterations(niter) smoothFilter.SetRelaxationFactor(relaxfact) smoothFilter.SetEdgeAngle(edgeAngle) smoothFilter.SetFeatureAngle(featureAngle) smoothFilter.BoundarySmoothingOn() smoothFilter.FeatureEdgeSmoothingOn() smoothFilter.GenerateErrorScalarsOn() smoothFilter.Update() return align(smoothFilter.GetOutput(), poly)
def smoothWSinc(actor, niter=15, passBand=0.1, edgeAngle=15, featureAngle=60): ''' Adjust mesh point positions using the windowed sinc function interpolation kernel. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/mesh_smoothers.py) ''' poly = vu.polydata(actor) cl = vtk.vtkCleanPolyData() vu.setInput(cl, poly) cl.Update() poly = cl.GetOutput() # removes the boudaries duplication smoothFilter = vtk.vtkWindowedSincPolyDataFilter() vu.setInput(smoothFilter, poly) smoothFilter.SetNumberOfIterations(niter) smoothFilter.SetEdgeAngle(edgeAngle) smoothFilter.SetFeatureAngle(featureAngle) smoothFilter.SetPassBand(passBand) smoothFilter.NormalizeCoordinatesOn() smoothFilter.NonManifoldSmoothingOn() smoothFilter.FeatureEdgeSmoothingOn() smoothFilter.BoundarySmoothingOn() smoothFilter.Update() return align(smoothFilter.GetOutput(), poly)
def keypress(vp, obj, event): key = obj.GetKeySym() #print ('Pressed key:', key, event) if key == "q" or key == "space" or key == "Return": vp.interactor.ExitCallback() return elif key == "e": if vp.verbose: print("closing window...") vp.interactor.GetRenderWindow().Finalize() vp.interactor.TerminateApp() del vp.renderWin, vp.interactor return elif key == "Escape": vp.interactor.TerminateApp() vp.interactor.GetRenderWindow().Finalize() vp.interactor.TerminateApp() del vp.renderWin, vp.interactor sys.exit(0) elif key == "S": colors.printc('Saving window as screenshot.png', 'green') vp.screenshot('screenshot.png') return elif key == "C": cam = vp.renderer.GetActiveCamera() print('\n### Example code to position this vtkCamera:') print('vp = vtkplotter.Plotter()\n...') print('vp.camera.SetPosition(', [round(e, 3) for e in cam.GetPosition()], ')') print('vp.camera.SetFocalPoint(', [round(e, 3) for e in cam.GetFocalPoint()], ')') print('vp.camera.SetViewUp(', [round(e, 3) for e in cam.GetViewUp()], ')') print('vp.camera.SetDistance(', round(cam.GetDistance(), 3), ')') print('vp.camera.SetClippingRange(', [round(e, 3) for e in cam.GetClippingRange()], ')') return elif key == "w": if vp.clickedActor and vp.clickedActor in vp.getActors(): vp.clickedActor.GetProperty().SetRepresentationToWireframe() else: for a in vp.getActors(): if a: a.GetProperty().SetRepresentationToWireframe() elif key == "s": if vp.clickedActor and vp.clickedActor in vp.getActors(): vp.clickedActor.GetProperty().SetRepresentationToSurface() else: for a in vp.getActors(): if a: a.GetProperty().SetRepresentationToSurface() elif key == "m": if vp.clickedActor in vp.getActors(): vp.clickedActor.GetProperty().SetOpacity(0.05) bfp = vp.clickedActor.GetBackfaceProperty() if bfp: bfp.SetOpacity(0.05) else: for a in vp.getActors(): a.GetProperty().SetOpacity(.05) bfp = a.GetBackfaceProperty() if bfp: bfp.SetOpacity(0.05) elif key == "comma": if vp.clickedActor in vp.getActors(): ap = vp.clickedActor.GetProperty() ap.SetOpacity(max([ap.GetOpacity() - 0.05, 0.05])) bfp = vp.clickedActor.GetBackfaceProperty() if bfp: bfp.SetOpacity(ap.GetOpacity()) else: for a in vp.getActors(): ap = a.GetProperty() ap.SetOpacity(max([ap.GetOpacity() - 0.05, 0.05])) bfp = a.GetBackfaceProperty() if bfp: bfp.SetOpacity(ap.GetOpacity()) elif key == "period": if vp.clickedActor in vp.getActors(): ap = vp.clickedActor.GetProperty() ap.SetOpacity(min([ap.GetOpacity() + 0.05, 1.0])) bfp = vp.clickedActor.GetBackfaceProperty() if bfp: bfp.SetOpacity(ap.GetOpacity()) else: for a in vp.getActors(): ap = a.GetProperty() ap.SetOpacity(min([ap.GetOpacity() + 0.05, 1.0])) bfp = a.GetBackfaceProperty() if bfp: bfp.SetOpacity(ap.GetOpacity()) elif key == "slash": if vp.clickedActor in vp.getActors(): vp.clickedActor.GetProperty().SetOpacity(1) bfp = vp.clickedActor.GetBackfaceProperty() if bfp: bfp.SetOpacity(1) else: for a in vp.getActors(): a.GetProperty().SetOpacity(1) bfp = a.GetBackfaceProperty() if bfp: bfp.SetOpacity(1) elif key == "V": if not (vp.verbose): vp._tips() vp.verbose = not (vp.verbose) print("Verbose: ", vp.verbose) elif key in ["1", "KP_End", "KP_1"]: for i, ia in enumerate(vp.getActors()): ia.GetProperty().SetColor(colors.colors1[(i + vp.icol1) % 10]) ia.GetMapper().ScalarVisibilityOff() vp.icol1 += 1 vp._draw_legend() elif key in ["2", "KP_Down", "KP_2"]: for i, ia in enumerate(vp.getActors()): ia.GetProperty().SetColor(colors.colors2[(i + vp.icol2) % 10]) ia.GetMapper().ScalarVisibilityOff() vp.icol2 += 1 vp._draw_legend() elif key in ["3", "KP_Left", "KP_4"]: for i, ia in enumerate(vp.getActors()): ia.GetProperty().SetColor(colors.colors3[(i + vp.icol3) % 10]) ia.GetMapper().ScalarVisibilityOff() vp.icol3 += 1 vp._draw_legend() elif key in ["4", "KP_Begin", "KP_5"]: c = colors.getColor('gold') acs = vp.getActors() alpha = 1. / len(acs) for ia in acs: ia.GetProperty().SetColor(c) ia.GetProperty().SetOpacity(alpha) ia.GetMapper().ScalarVisibilityOff() vp._draw_legend() elif key in ["k", "K"]: for a in vp.getActors(): ptdata = utils.polydata(a).GetPointData() cldata = utils.polydata(a).GetCellData() arrtypes = dict() arrtypes[vtk.VTK_UNSIGNED_CHAR] = 'VTK_UNSIGNED_CHAR' arrtypes[vtk.VTK_UNSIGNED_INT] = 'VTK_UNSIGNED_INT' arrtypes[vtk.VTK_FLOAT] = 'VTK_FLOAT' arrtypes[vtk.VTK_DOUBLE] = 'VTK_DOUBLE' foundarr = 0 if key == 'k': for i in range(ptdata.GetNumberOfArrays()): name = ptdata.GetArrayName(i) if name == 'Normals': continue if hasattr(a, 'legend'): print('actor:', a.legend, end='') print('\tfound POINT array with name:', name, end=', ') print('type:', arrtypes[ptdata.GetArray(i).GetDataType()]) ptdata.SetActiveScalars(name) foundarr = 1 if not foundarr: print('No vtkArray is associated to points', end='') if hasattr(a, 'legend'): print(' for actor:', a.legend) else: print() if key == 'K': for i in range(cldata.GetNumberOfArrays()): name = cldata.GetArrayName(i) if name == 'Normals': continue if hasattr(a, 'legend'): print('actor:', a.legend, end='') print('\tfound POINT array', 'tname:', name, end=', ') print('type:', arrtypes[cldata.GetArray(i).GetDataType()]) cldata.SetActiveScalars(name) foundarr = 1 if not foundarr: print('No vtkArray is associated to cells', end='') if hasattr(a, 'legend'): print(' for actor:', a.legend) else: print() a.GetMapper().ScalarVisibilityOn() elif key == "P": if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: try: ps = ia.GetProperty().GetPointSize() if ps > 1: ia.GetProperty().SetPointSize(ps - 1) ia.GetProperty().SetRepresentationToPoints() except AttributeError: pass elif key == "p": if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: try: ps = ia.GetProperty().GetPointSize() ia.GetProperty().SetPointSize(ps + 2) ia.GetProperty().SetRepresentationToPoints() except AttributeError: pass elif key == "L": if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: try: ia.GetProperty().SetRepresentationToSurface() ls = ia.GetProperty().GetLineWidth() if ls <= 1: ls = 1 ia.GetProperty().EdgeVisibilityOff() else: ia.GetProperty().EdgeVisibilityOn() ia.GetProperty().SetLineWidth(ls - 1) except AttributeError: pass elif key == "l": if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: try: ia.GetProperty().EdgeVisibilityOn() c = ia.GetProperty().GetColor() ia.GetProperty().SetEdgeColor(c) ls = ia.GetProperty().GetLineWidth() ia.GetProperty().SetLineWidth(ls + 1) except AttributeError: pass elif key == "n": # show normals to an actor if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: alpha = ia.GetProperty().GetOpacity() c = ia.GetProperty().GetColor() a = vp.normals(ia, ratio=1, c=c, alpha=alpha) vp.actors.pop() #remove from list try: i = vp.actors.index(ia) vp.actors[i] = a vp.renderer.RemoveActor(ia) vp.interactor.Render() except ValueError: pass vp.render(a) elif key == "x": if vp.justremoved is None: if vp.clickedActor in vp.getActors() or isinstance( vp.clickedActor, vtk.vtkAssembly): vp.justremoved = vp.clickedActor vp.renderer.RemoveActor(vp.clickedActor) if hasattr(vp.clickedActor, 'legend') and vp.clickedActor.legend: colors.printc(' ...removing actor: ' + str(vp.clickedActor.legend) + ', press x to put it back') else: if vp.verbose: colors.printc('Click an actor and press x to toggle it.', 5) else: vp.renderer.AddActor(vp.justremoved) vp.renderer.Render() vp.justremoved = None vp._draw_legend() elif key == "X": if vp.clickedActor: if hasattr(vp.clickedActor, 'legend') and vp.clickedActor.legend: fname = 'clipped_' + str(vp.clickedActor.legend) fname = fname.split('.')[0] + '.vtk' else: fname = 'clipped.vtk' if vp.verbose: colors.printc('Move handles to remove part of the actor.', 4) utils.cutterWidget(vp.clickedActor, fname) elif vp.verbose: colors.printc( 'Click an actor and press X to open the cutter box widget.', 4) elif key == "r": vp.renderer.ResetCamera() if vp.keyPressFunction: if key not in ['Shift_L', 'Control_L', 'Super_L', 'Alt_L']: if key not in ['Shift_R', 'Control_R', 'Super_R', 'Alt_R']: vp.keyPressFunction(key, vp) if vp.interactor: vp.interactor.Render()
def _keypress(vp, obj, event): key = obj.GetKeySym() #print ('Pressed key:', key, event) if key == "q" or key == "space" or key == "Return": vp.interactor.ExitCallback() return elif key == "e": if vp.verbose: print("closing window...") vp.interactor.GetRenderWindow().Finalize() vp.interactor.TerminateApp() return elif key == "Escape": print() sys.stdout.flush() vp.interactor.TerminateApp() vp.interactor.GetRenderWindow().Finalize() vp.interactor.TerminateApp() sys.exit(0) elif key == "m": if vp.clickedActor in vp.getActors(): vp.clickedActor.GetProperty().SetOpacity(0.02) bfp = vp.clickedActor.GetBackfaceProperty() if bfp: bfp.SetOpacity(0.02) else: for a in vp.getActors(): a.GetProperty().SetOpacity(0.02) bfp = a.GetBackfaceProperty() if bfp: bfp.SetOpacity(0.02) elif key == "slash": if vp.clickedActor in vp.getActors(): vp.clickedActor.GetProperty().SetOpacity(1) bfp = vp.clickedActor.GetBackfaceProperty() if bfp: bfp.SetOpacity(1) else: for a in vp.getActors(): a.GetProperty().SetOpacity(1) bfp = a.GetBackfaceProperty() if bfp: bfp.SetOpacity(1) elif key == "comma": if vp.clickedActor in vp.getActors(): ap = vp.clickedActor.GetProperty() ap.SetOpacity(max([ap.GetOpacity() * 0.75, 0.01])) bfp = vp.clickedActor.GetBackfaceProperty() if bfp: bfp.SetOpacity(ap.GetOpacity()) else: for a in vp.getActors(): ap = a.GetProperty() ap.SetOpacity(max([ap.GetOpacity() * 0.75, 0.01])) bfp = a.GetBackfaceProperty() if bfp: bfp.SetOpacity(ap.GetOpacity()) elif key == "period": if vp.clickedActor in vp.getActors(): ap = vp.clickedActor.GetProperty() ap.SetOpacity(min([ap.GetOpacity() * 1.25, 1.0])) bfp = vp.clickedActor.GetBackfaceProperty() if bfp: bfp.SetOpacity(ap.GetOpacity()) else: for a in vp.getActors(): ap = a.GetProperty() ap.SetOpacity(min([ap.GetOpacity() * 1.25, 1.0])) bfp = a.GetBackfaceProperty() if bfp: bfp.SetOpacity(ap.GetOpacity()) elif key == "P": if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: try: ps = ia.GetProperty().GetPointSize() if ps > 1: ia.GetProperty().SetPointSize(ps - 1) ia.GetProperty().SetRepresentationToPoints() except AttributeError: pass elif key == "p": if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: try: ps = ia.GetProperty().GetPointSize() ia.GetProperty().SetPointSize(ps + 2) ia.GetProperty().SetRepresentationToPoints() except AttributeError: pass elif key == "w": if vp.clickedActor and vp.clickedActor in vp.getActors(): vp.clickedActor.GetProperty().SetRepresentationToWireframe() else: for a in vp.getActors(): if a: a.GetProperty().SetRepresentationToWireframe() elif key == "r": vp.renderer.ResetCamera() ### now intercept custom observer ########################### if vp.keyPressFunction: if key not in ['Shift_L', 'Control_L', 'Super_L', 'Alt_L']: if key not in ['Shift_R', 'Control_R', 'Super_R', 'Alt_R']: vp.verbose = False vp.keyPressFunction(key) return if key == "S": vp.screenshot('screenshot.png') vc.printc('Saved rendering window as screenshot.png', c='blue') return elif key == "C": cam = vp.renderer.GetActiveCamera() print('\n### Example code to position this vtkCamera:') print('vp = vtkplotter.Plotter()\n...') print('vp.camera.SetPosition(', [round(e, 3) for e in cam.GetPosition()], ')') print('vp.camera.SetFocalPoint(', [round(e, 3) for e in cam.GetFocalPoint()], ')') print('vp.camera.SetViewUp(', [round(e, 3) for e in cam.GetViewUp()], ')') print('vp.camera.SetDistance(', round(cam.GetDistance(), 3), ')') print('vp.camera.SetClippingRange(', [round(e, 3) for e in cam.GetClippingRange()], ')') return elif key == "s": if vp.clickedActor and vp.clickedActor in vp.getActors(): vp.clickedActor.GetProperty().SetRepresentationToSurface() else: for a in vp.getActors(): if a: a.GetProperty().SetRepresentationToSurface() elif key == "V": if not (vp.verbose): vp._tips() vp.verbose = not (vp.verbose) print("Verbose: ", vp.verbose) elif key in ["1", "KP_End", "KP_1"]: if vp.clickedActor and hasattr(vp.clickedActor, 'GetProperty'): vp.clickedActor.GetProperty().SetColor(vc.colors1[(vp.icol) % 10]) else: for i, ia in enumerate(vp.getActors()): ia.GetProperty().SetColor(vc.colors1[(i + vp.icol) % 10]) ia.GetMapper().ScalarVisibilityOff() vp.icol += 1 vp._draw_legend() elif key in ["2", "KP_Down", "KP_2"]: if vp.clickedActor and hasattr(vp.clickedActor, 'GetProperty'): vp.clickedActor.GetProperty().SetColor(vc.colors2[(vp.icol) % 10]) else: for i, ia in enumerate(vp.getActors()): ia.GetProperty().SetColor(vc.colors2[(i + vp.icol) % 10]) ia.GetMapper().ScalarVisibilityOff() vp.icol += 1 vp._draw_legend() elif key in ["3", "KP_Left", "KP_4"]: c = vc.getColor('gold') acs = vp.getActors() alpha = 1. / len(acs) for ia in acs: ia.GetProperty().SetColor(c) ia.GetProperty().SetOpacity(alpha) ia.GetMapper().ScalarVisibilityOff() vp._draw_legend() elif key in ["k", "K"]: for a in vp.getActors(): ptdata = vu.polydata(a).GetPointData() cldata = vu.polydata(a).GetCellData() arrtypes = dict() arrtypes[vtk.VTK_UNSIGNED_CHAR] = 'VTK_UNSIGNED_CHAR' arrtypes[vtk.VTK_UNSIGNED_INT] = 'VTK_UNSIGNED_INT' arrtypes[vtk.VTK_FLOAT] = 'VTK_FLOAT' arrtypes[vtk.VTK_DOUBLE] = 'VTK_DOUBLE' foundarr = 0 if key == 'k': for i in range(ptdata.GetNumberOfArrays()): name = ptdata.GetArrayName(i) if name == 'Normals': continue ptdata.SetActiveScalars(name) foundarr = 1 if not foundarr: print('No vtkArray is associated to points', end='') if hasattr(a, 'legend'): print(' for actor:', a.legend) else: print() if key == 'K': for i in range(cldata.GetNumberOfArrays()): name = cldata.GetArrayName(i) if name == 'Normals': continue cldata.SetActiveScalars(name) foundarr = 1 if not foundarr: print('No vtkArray is associated to cells', end='') if hasattr(a, 'legend'): print(' for actor:', a.legend) else: print() a.GetMapper().ScalarVisibilityOn() elif key == "L": if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: try: ia.GetProperty().SetRepresentationToSurface() ls = ia.GetProperty().GetLineWidth() if ls <= 1: ls = 1 ia.GetProperty().EdgeVisibilityOff() else: ia.GetProperty().EdgeVisibilityOn() ia.GetProperty().SetLineWidth(ls - 1) except AttributeError: pass elif key == "l": if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: try: ia.GetProperty().EdgeVisibilityOn() c = ia.GetProperty().GetColor() ia.GetProperty().SetEdgeColor(c) ls = ia.GetProperty().GetLineWidth() ia.GetProperty().SetLineWidth(ls + 1) except AttributeError: pass elif key == "n": # show normals to an actor from vtkplotter.analysis import normals as ana_normals if vp.clickedActor in vp.getActors(): acts = [vp.clickedActor] else: acts = vp.getActors() for ia in acts: alpha = ia.GetProperty().GetOpacity() c = ia.GetProperty().GetColor() a = ana_normals(ia, ratio=1, c=c, alpha=alpha) try: i = vp.actors.index(ia) vp.actors[i] = a vp.renderer.RemoveActor(ia) vp.interactor.Render() except ValueError: pass vp.render(a) elif key == "x": if vp.justremoved is None: if vp.clickedActor in vp.getActors() or isinstance( vp.clickedActor, vtk.vtkAssembly): vp.justremoved = vp.clickedActor vp.renderer.RemoveActor(vp.clickedActor) if hasattr(vp.clickedActor, 'legend') and vp.clickedActor.legend: vc.printc('...removing actor: ' + str(vp.clickedActor.legend) + ', press x to put it back') else: if vp.verbose: vc.printc('Click an actor and press x to toggle it.', c=5) else: vp.renderer.AddActor(vp.justremoved) vp.renderer.Render() vp.justremoved = None vp._draw_legend() elif key == "X": if len(vp.actors): vp.clickedActor = vp.actors[-1] if vp.clickedActor: if not vp.cutterWidget: vp.cutterWidget = vp.addCutterTool(vp.clickedActor) else: fname = 'clipped.vtk' confilter = vtk.vtkPolyDataConnectivityFilter() vu.setInput(confilter, vu.polydata(vp.clickedActor, True)) confilter.SetExtractionModeToLargestRegion() confilter.Update() cpd = vtk.vtkCleanPolyData() vu.setInput(cpd, confilter.GetOutput()) cpd.Update() w = vtk.vtkPolyDataWriter() vu.setInput(w, cpd.GetOutput()) w.SetFileName(fname) w.Write() vc.printc(" -> Saved file:", fname, c='m') vp.cutterWidget.Off() vp.cutterWidget = None else: vc.printc( 'Click an actor and press X to open the cutter box widget.', c=4) elif key == 'i': # print info vu.printInfo(vp.clickedActor) if vp.interactor: vp.interactor.Render()
def fxy(z='sin(3*x)*log(x-y)/3', x=[0, 3], y=[0, 3], zlimits=[None, None], showNan=True, zlevels=10, wire=False, c='b', bc='aqua', alpha=1, legend=True, texture=None, res=100): ''' Build a surface representing the 3D function specified as a string or as a reference to an external function. Red points indicate where the function does not exist (showNan). zlevels will draw the specified number of z-levels contour lines. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/fxy.py) ![fxy](https://user-images.githubusercontent.com/32848391/36611824-fd524fac-18d4-11e8-8c76-d3d1b1bb3954.png) ''' if isinstance(z, str): try: z = z.replace('math.', '').replace('np.', '') namespace = locals() code = "from math import*\ndef zfunc(x,y): return "+z exec(code, namespace) z = namespace['zfunc'] except: vc.printc('Syntax Error in fxy()', c=1) return None ps = vtk.vtkPlaneSource() ps.SetResolution(res, res) ps.SetNormal([0, 0, 1]) ps.Update() poly = ps.GetOutput() dx = x[1]-x[0] dy = y[1]-y[0] todel, nans = [], [] if zlevels: tf = vtk.vtkTriangleFilter() vu.setInput(tf, poly) tf.Update() poly = tf.GetOutput() for i in range(poly.GetNumberOfPoints()): px, py, _ = poly.GetPoint(i) xv = (px+.5)*dx+x[0] yv = (py+.5)*dy+y[0] try: zv = z(xv, yv) poly.GetPoints().SetPoint(i, [xv, yv, zv]) except: todel.append(i) nans.append([xv, yv, 0]) if len(todel): cellIds = vtk.vtkIdList() poly.BuildLinks() for i in todel: poly.GetPointCells(i, cellIds) for j in range(cellIds.GetNumberOfIds()): poly.DeleteCell(cellIds.GetId(j)) # flag cell poly.RemoveDeletedCells() cl = vtk.vtkCleanPolyData() vu.setInput(cl, poly) cl.Update() poly = cl.GetOutput() if not poly.GetNumberOfPoints(): vc.printc('Function is not real in the domain', c=1) return vtk.vtkActor() if zlimits[0]: a = vu.cutPlane(poly, (0, 0, zlimits[0]), (0, 0, 1)) poly = vu.polydata(a) if zlimits[1]: a = vu.cutPlane(poly, (0, 0, zlimits[1]), (0, 0, -1)) poly = vu.polydata(a) if c is None: elev = vtk.vtkElevationFilter() vu.setInput(elev, poly) elev.Update() poly = elev.GetOutput() actor = vu.makeActor(poly, c=c, bc=bc, alpha=alpha, wire=wire, legend=legend, texture=texture) acts = [actor] if zlevels: elevation = vtk.vtkElevationFilter() vu.setInput(elevation, poly) bounds = poly.GetBounds() elevation.SetLowPoint(0, 0, bounds[4]) elevation.SetHighPoint(0, 0, bounds[5]) elevation.Update() bcf = vtk.vtkBandedPolyDataContourFilter() vu.setInput(bcf, elevation.GetOutput()) bcf.SetScalarModeToValue() bcf.GenerateContourEdgesOn() bcf.GenerateValues(zlevels, elevation.GetScalarRange()) bcf.Update() zpoly = bcf.GetContourEdgesOutput() zbandsact = vu.makeActor(zpoly, c='k', alpha=alpha) zbandsact.GetProperty().SetLineWidth(1.5) acts.append(zbandsact) if showNan and len(todel): bb = actor.GetBounds() zm = (bb[4]+bb[5])/2 nans = np.array(nans)+[0, 0, zm] nansact = vs.points(nans, c='red', alpha=alpha/2) acts.append(nansact) if len(acts) > 1: asse = vu.makeAssembly(acts) return asse else: return actor
def smoothMLS2D(actor, f=0.2, decimate=1, recursive=0, showNPlanes=0): ''' Smooth actor or points with a Moving Least Squares variant. The list actor.variances contain the residue calculated for each point. Input actor's polydata is modified. Options: f, smoothing factor - typical range s [0,2] decimate, decimation factor (an integer number) recursive, move points while algorithm proceedes showNPlanes, build an actor showing the fitting plane for N random points [**Example1**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/mesh_smoothers.py) [**Example2**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/moving_least_squares2D.py) [**Example3**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/recosurface.py) ''' coords = vu.coordinates(actor) ncoords = len(coords) Ncp = int(ncoords*f/100) nshow = int(ncoords/decimate) if showNPlanes: ndiv = int(nshow/showNPlanes*decimate) if Ncp < 5: vc.printc('Please choose a higher fraction than '+str(f), c=1) Ncp = 5 print('smoothMLS: Searching #neighbours, #pt:', Ncp, ncoords) poly = vu.polydata(actor, True) vpts = poly.GetPoints() locator = vtk.vtkPointLocator() locator.SetDataSet(poly) locator.BuildLocator() vtklist = vtk.vtkIdList() variances, newsurf, acts = [], [], [] pb = vio.ProgressBar(0, ncoords) for i, p in enumerate(coords): pb.print('smoothing...') if i % decimate: continue locator.FindClosestNPoints(Ncp, p, vtklist) points = [] for j in range(vtklist.GetNumberOfIds()): trgp = [0, 0, 0] vpts.GetPoint(vtklist.GetId(j), trgp) points.append(trgp) if len(points) < 5: continue points = np.array(points) pointsmean = points.mean(axis=0) # plane center uu, dd, vv = np.linalg.svd(points-pointsmean) a, b, c = np.cross(vv[0], vv[1]) # normal d, e, f = pointsmean # plane center x, y, z = p t = (a*d - a*x + b*e - b*y + c*f - c*z) # /(a*a+b*b+c*c) newp = [x+t*a, y+t*b, z+t*c] variances.append(dd[2]) newsurf.append(newp) if recursive: vpts.SetPoint(i, newp) if showNPlanes and not i % ndiv: plane = fitPlane(points, alpha=0.3) # fitting plane iapts = vs.points(points) # blue points acts += [plane, iapts] if decimate == 1 and not recursive: for i in range(ncoords): vpts.SetPoint(i, newsurf[i]) setattr(actor, 'variances', np.array(variances)) if showNPlanes: apts = vs.points(newsurf, c='r 0.6', r=2) ass = vu.makeAssembly([apts]+acts) return ass # NB: a demo actor is returned return actor # NB: original actor is modified