def pca(points, pvalue=.95, c='c', alpha=0.5, pcaAxes=False, legend=None): ''' Show the oriented PCA ellipsoid that contains fraction pvalue of points. axes = True, show the 3 PCA semi axes Extra info is stored in actor.sphericity, actor.va, actor.vb, actor.vc (sphericity = 1 for a perfect sphere) ''' try: from scipy.stats import f except: vc.printc("Error in ellipsoid(): scipy not installed. Skip.",1) return None if isinstance(points, vtk.vtkActor): points=vu.coordinates(points) if len(points) == 0: return None P = np.array(points, ndmin=2, dtype=float) cov = np.cov(P, rowvar=0) # covariance matrix U, s, R = np.linalg.svd(cov) # singular value decomposition p, n = s.size, P.shape[0] fppf = f.ppf(pvalue, p, n-p)*(n-1)*p*(n+1)/n/(n-p) # f % point function ua,ub,uc = np.sqrt(s*fppf)*2 # semi-axes (largest first) center = np.mean(P, axis=0) # centroid of the hyperellipsoid sphericity = ( ((ua-ub)/(ua+ub))**2 + ((ua-uc)/(ua+uc))**2 + ((ub-uc)/(ub+uc))**2 )/3. *4. elliSource = vtk.vtkSphereSource() elliSource.SetThetaResolution(48) elliSource.SetPhiResolution(48) matri = vtk.vtkMatrix4x4() matri.DeepCopy((R[0][0] *ua, R[1][0] *ub, R[2][0] *uc, center[0], R[0][1] *ua, R[1][1] *ub, R[2][1] *uc, center[1], R[0][2] *ua, R[1][2] *ub, R[2][2] *uc, center[2], 0,0,0,1)) vtra = vtk.vtkTransform() vtra.SetMatrix(matri) ftra = vtk.vtkTransformFilter() ftra.SetTransform(vtra) ftra.SetInputConnection(elliSource.GetOutputPort()) ftra.Update() actor_elli = vu.makeActor(ftra.GetOutput(), c, alpha, legend=legend) actor_elli.GetProperty().BackfaceCullingOn() actor_elli.GetProperty().SetInterpolationToPhong() if pcaAxes: axs = [] for ax in ([1,0,0], [0,1,0], [0,0,1]): l = vtk.vtkLineSource() l.SetPoint1([0,0,0]) l.SetPoint2(ax) l.Update() t = vtk.vtkTransformFilter() t.SetTransform(vtra) vu.setInput(t, l.GetOutput()) t.Update() axs.append(vu.makeActor(t.GetOutput(), c, alpha)) finact = vu.makeAssembly([actor_elli]+axs, legend=legend) else : finact = actor_elli setattr(finact, 'sphericity', sphericity) setattr(finact, 'va', ua) setattr(finact, 'vb', ub) setattr(finact, 'vc', uc) return finact
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 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 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 recoSurface(points, bins=256, c='gold', alpha=1, wire=False, bc='t', edges=False, legend=None): ''' Surface reconstruction from sparse points. ''' if isinstance(points, vtk.vtkActor): points = vu.coordinates(points) N = len(points) if N < 50: print('recoSurface: Use at least 50 points.') return None points = np.array(points) ptsSource = vtk.vtkPointSource() ptsSource.SetNumberOfPoints(N) ptsSource.Update() vpts = ptsSource.GetOutput().GetPoints() for i, p in enumerate(points): vpts.SetPoint(i, p) polyData = ptsSource.GetOutput() distance = vtk.vtkSignedDistance() f = 0.1 x0, x1, y0, y1, z0, z1 = polyData.GetBounds() distance.SetBounds(x0 - (x1 - x0) * f, x1 + (x1 - x0) * f, y0 - (y1 - y0) * f, y1 + (y1 - y0) * f, z0 - (z1 - z0) * f, z1 + (z1 - z0) * f) if polyData.GetPointData().GetNormals(): distance.SetInputData(polyData) vu.setInput(distance, polyData) else: normals = vtk.vtkPCANormalEstimation() vu.setInput(normals, polyData) normals.SetSampleSize(int(N / 50)) normals.SetNormalOrientationToGraphTraversal() distance.SetInputConnection(normals.GetOutputPort()) print('Recalculating normals for', N, 'points, sample size=', int(N / 50)) radius = vu.diagonalSize(polyData) / bins * 5 distance.SetRadius(radius) distance.SetDimensions(bins, bins, bins) distance.Update() print('Calculating mesh from points with R =', radius) surface = vtk.vtkExtractSurface() surface.SetRadius(radius * .99) surface.HoleFillingOn() surface.ComputeNormalsOff() surface.ComputeGradientsOff() surface.SetInputConnection(distance.GetOutputPort()) surface.Update() return vu.makeActor(surface.GetOutput(), c, alpha, wire, bc, edges, legend)
def arrow(startPoint, endPoint, c, s=None, alpha=1, legend=None, texture=None, res=12, rwSize=None): '''Build a 3D arrow from startPoint to endPoint of section size s, expressed as the fraction of the window size. If s=None the arrow is scaled proportionally to its length.''' axis = np.array(endPoint) - np.array(startPoint) length = np.linalg.norm(axis) if not length: return None axis = axis / length theta = np.arccos(axis[2]) phi = np.arctan2(axis[1], axis[0]) arr = vtk.vtkArrowSource() arr.SetShaftResolution(res) arr.SetTipResolution(res) if s: sz = 0.02 arr.SetTipRadius(sz) arr.SetShaftRadius(sz / 1.75) arr.SetTipLength(sz * 15) arr.Update() t = vtk.vtkTransform() t.RotateZ(phi * 57.3) t.RotateY(theta * 57.3) t.RotateY(-90) #put it along Z if s: w, h = rwSize sz = (w + h) / 2 * s t.Scale(length, sz, sz) else: t.Scale(length, length, length) tf = vtk.vtkTransformPolyDataFilter() vu.setInput(tf, arr.GetOutput()) tf.SetTransform(t) tf.Update() actor = vu.makeActor(tf.GetOutput(), c, alpha, legend=legend, texture=texture) actor.GetProperty().SetInterpolationToPhong() actor.SetPosition(startPoint) actor.DragableOff() actor.PickableOff() setattr(actor, 'base', np.array(startPoint)) setattr(actor, 'top', np.array(endPoint)) return actor
def loadPolyData(filename): '''Load a file and return a vtkPolyData object (not a vtkActor).''' if not os.path.exists(filename): vc.printc('Error in loadPolyData: Cannot find', filename, c=1) return None fl = filename.lower() if fl.endswith('.vtk'): reader = vtk.vtkPolyDataReader() elif fl.endswith('.ply'): reader = vtk.vtkPLYReader() elif fl.endswith('.obj'): reader = vtk.vtkOBJReader() elif fl.endswith('.stl'): reader = vtk.vtkSTLReader() elif fl.endswith('.byu') or fl.endswith('.g'): reader = vtk.vtkBYUReader() elif fl.endswith('.vtp'): reader = vtk.vtkXMLPolyDataReader() elif fl.endswith('.vts'): reader = vtk.vtkXMLStructuredGridReader() elif fl.endswith('.vtu'): reader = vtk.vtkXMLUnstructuredGridReader() elif fl.endswith('.txt'): reader = vtk.vtkParticleReader() # (x y z scalar) elif fl.endswith('.xyz'): reader = vtk.vtkParticleReader() else: reader = vtk.vtkDataReader() reader.SetFileName(filename) if fl.endswith('.vts'): # structured grid reader.Update() gf = vtk.vtkStructuredGridGeometryFilter() gf.SetInputConnection(reader.GetOutputPort()) gf.Update() poly = gf.GetOutput() elif fl.endswith('.vtu'): # unstructured grid reader.Update() gf = vtk.vtkGeometryFilter() gf.SetInputConnection(reader.GetOutputPort()) gf.Update() poly = gf.GetOutput() else: try: reader.Update() poly = reader.GetOutput() except: poly = None if not poly: return None cleanpd = vtk.vtkCleanPolyData() vu.setInput(cleanpd, poly) cleanpd.Update() return cleanpd.GetOutput()
def line(p0, p1=None, lw=1, tube=False, dotted=False, c='r', alpha=1., legend=None): '''Build the line segment between points p0 and p1. if p0 is a list of points returns the line connecting them. if tube=True, lines are rendered as tubes of radius lw ''' #detect if user is passing a list of points: if vu.isSequence(p0[0]): ppoints = vtk.vtkPoints() # Generate the polyline poly = vtk.vtkPolyData() for i in range(len(p0)): p = p0[i] ppoints.InsertPoint(i, p[0], p[1], p[2]) lines = vtk.vtkCellArray() # Create the polyline. lines.InsertNextCell(len(p0)) for i in range(len(p0)): lines.InsertCellPoint(i) poly.SetPoints(ppoints) poly.SetLines(lines) else: # or just 2 points to link lineSource = vtk.vtkLineSource() lineSource.SetPoint1(p0) lineSource.SetPoint2(p1) lineSource.Update() poly = lineSource.GetOutput() if tube: tuf = vtk.vtkTubeFilter() tuf.SetNumberOfSides(12) #tuf.CappingOn() vu.setInput(tuf, poly) tuf.SetRadius(lw) tuf.Update() poly = tuf.GetOutput() actor = vu.makeActor(poly, c, alpha, legend=legend) actor.GetProperty().SetInterpolationToPhong() else: actor = vu.makeActor(poly, c, alpha, legend=legend) actor.GetProperty().SetLineWidth(lw) if dotted: actor.GetProperty().SetLineStipplePattern(0xf0f0) actor.GetProperty().SetLineStippleRepeatFactor(1) setattr(actor, 'base', np.array(p0)) setattr(actor, 'top', np.array(p1)) return actor
def polygon(pos=[0, 0, 0], normal=[0, 0, 1], nsides=6, r=1, c='coral', bc='darkgreen', lw=1, alpha=1, legend=None, texture=None, followcam=False, camera=None): '''Build a 2D polygon of nsides of radius r oriented as normal If followcam=True the polygon will always reorient itself to current camera. ''' ps = vtk.vtkRegularPolygonSource() ps.SetNumberOfSides(nsides) ps.SetRadius(r) ps.SetNormal(-np.array(normal)) ps.Update() tf = vtk.vtkTriangleFilter() vu.setInput(tf, ps.GetOutputPort()) tf.Update() mapper = vtk.vtkPolyDataMapper() vu.setInput(mapper, tf.GetOutputPort()) if followcam: #follow cam actor = vtk.vtkFollower() actor.SetCamera(camera) if not camera: vio.printc('Warning: vtkCamera does not yet exist for polygon', 5) else: actor = vtk.vtkActor() actor.SetMapper(mapper) actor.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 actor.GetProperty().SetOpacity(alpha) actor.GetProperty().SetLineWidth(lw) actor.GetProperty().SetInterpolationToFlat() if bc: # defines a specific color for the backface backProp = vtk.vtkProperty() backProp.SetDiffuseColor(vc.getColor(bc)) backProp.SetOpacity(alpha) actor.SetBackfaceProperty(backProp) if texture: vu.assignTexture(actor, texture) vu.assignPhysicsMethods(actor) vu.assignConvenienceMethods(actor, legend) actor.SetPosition(pos) 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 delaunay2D(plist, tol=None, c='gold', alpha=0.5, wire=False, bc=None, edges=False, legend=None, texture=None): ''' Create a mesh from points in the XY plane. ''' src = vtk.vtkPointSource() src.SetNumberOfPoints(len(plist)) src.Update() pd = src.GetOutput() for i,p in enumerate(plist): pd.GetPoints().SetPoint(i, p) delny = vtk.vtkDelaunay2D() vu.setInput(delny, pd) if tol: delny.SetTolerance(tol) delny.Update() return vu.makeActor(delny.GetOutput(), c, alpha, wire, bc, edges, legend, texture)
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 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 hyperboloid(pos=[0, 0, 0], a2=1, value=0.5, height=1, axis=[0, 0, 1], c='magenta', alpha=1, legend=None, texture=None, res=50): ''' Build a hyperboloid of specified aperture a2 and height, centered at pos. ''' q = vtk.vtkQuadric() q.SetCoefficients(2, 2, -1 / a2, 0, 0, 0, 0, 0, 0, 0) #F(x,y,z) = a0*x^2 + a1*y^2 + a2*z^2 # + a3*x*y + a4*y*z + a5*x*z # + a6*x + a7*y + a8*z +a9 sample = vtk.vtkSampleFunction() sample.SetSampleDimensions(res, res, res) sample.SetImplicitFunction(q) contours = vtk.vtkContourFilter() contours.SetInputConnection(sample.GetOutputPort()) contours.GenerateValues(1, value, value) contours.Update() axis = np.array(axis) / np.linalg.norm(axis) theta = np.arccos(axis[2]) phi = np.arctan2(axis[1], axis[0]) t = vtk.vtkTransform() t.PostMultiply() t.RotateY(theta * 57.3) t.RotateZ(phi * 57.3) t.Scale(1, 1, height) tf = vtk.vtkTransformPolyDataFilter() vu.setInput(tf, contours.GetOutput()) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() actor = vu.makeActor(pd, c=c, alpha=alpha, legend=legend, texture=texture) actor.GetProperty().SetInterpolationToPhong() actor.GetMapper().ScalarVisibilityOff() actor.SetPosition(pos) return actor
def load2Dimage(filename, alpha): fl = filename.lower() if '.png' in fl: picr = vtk.vtkPNGReader() elif '.jpg' in fl or '.jpeg' in fl: picr = vtk.vtkJPEGReader() else: print('file must end with .png or .jpg') exit(1) picr.SetFileName(filename) picr.Update() vactor = vtk.vtkImageActor() vu.setInput(vactor, picr.GetOutput()) vactor.SetOpacity(alpha) vu.assignConvenienceMethods(vactor, False) vu.assignPhysicsMethods(vactor) return vactor
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 ring(pos=[0, 0, 0], r=1, thickness=0.1, axis=[0, 0, 1], c='khaki', alpha=1, wire=False, legend=None, texture=None, res=30): ''' Build a torus of specified outer radius r internal radius thickness, centered at pos. ''' rs = vtk.vtkParametricTorus() rs.SetRingRadius(r) rs.SetCrossSectionRadius(thickness) pfs = vtk.vtkParametricFunctionSource() pfs.SetParametricFunction(rs) pfs.SetUResolution(res * 3) pfs.SetVResolution(res) pfs.Update() nax = np.linalg.norm(axis) if nax: axis = np.array(axis) / nax theta = np.arccos(axis[2]) phi = np.arctan2(axis[1], axis[0]) t = vtk.vtkTransform() t.PostMultiply() t.RotateY(theta * 57.3) t.RotateZ(phi * 57.3) tf = vtk.vtkTransformPolyDataFilter() vu.setInput(tf, pfs.GetOutput()) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() actor = vu.makeActor(pd, c=c, alpha=alpha, wire=wire, legend=legend, texture=texture) actor.GetProperty().SetInterpolationToPhong() actor.SetPosition(pos) return actor
def ellipsoid(pos=[0, 0, 0], axis1=[1, 0, 0], axis2=[0, 2, 0], axis3=[0, 0, 3], c='c', alpha=1, legend=None, texture=None, res=24): """ Build a 3D ellipsoid centered at position pos. Axis1 and axis2 are only used to define sizes and one azimuth angle """ elliSource = vtk.vtkSphereSource() elliSource.SetThetaResolution(res) elliSource.SetPhiResolution(res) elliSource.Update() l1 = np.linalg.norm(axis1) l2 = np.linalg.norm(axis2) l3 = np.linalg.norm(axis3) axis1 = np.array(axis1) / l1 axis2 = np.array(axis2) / l2 axis3 = np.array(axis3) / l3 angle = np.arcsin(np.dot(axis1, axis2)) theta = np.arccos(axis3[2]) phi = np.arctan2(axis3[1], axis3[0]) t = vtk.vtkTransform() t.PostMultiply() t.Scale(l1, l2, l3) t.RotateX(angle * 57.3) t.RotateY(theta * 57.3) t.RotateZ(phi * 57.3) tf = vtk.vtkTransformPolyDataFilter() vu.setInput(tf, elliSource.GetOutput()) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() actor = vu.makeActor(pd, c=c, alpha=alpha, legend=legend, texture=texture) actor.GetProperty().BackfaceCullingOn() actor.GetProperty().SetInterpolationToPhong() actor.SetPosition(pos) return actor
def buildPolyData(vertices, faces=None, indexOffset=0): ''' Build a vtkPolyData object from a list of vertices and the connectivity representing the faces of the polygonal mesh. E.g. faces=[[0,1,2], [1,2,3], ...], vertices=[[x1,y1,z1],[x2,y2,z2], ...] Use indexOffset=1 if face numbering starts from 1 instead of 0. ''' sourcePoints = vtk.vtkPoints() sourceVertices = vtk.vtkCellArray() sourcePolygons = vtk.vtkCellArray() for pt in vertices: if len(pt) > 2: aid = sourcePoints.InsertNextPoint(pt[0], pt[1], pt[2]) else: aid = sourcePoints.InsertNextPoint(pt[0], pt[1], 0) sourceVertices.InsertNextCell(1) sourceVertices.InsertCellPoint(aid) if faces: for f in faces: n = len(f) if n == 4: plg = vtk.vtkTetra() elif n == 3: plg = vtk.vtkTriangle() else: plg = vtk.vtkPolygon() plg.GetPointIds().SetNumberOfIds(n) for i in range(n): plg.GetPointIds().SetId(i, f[i] - indexOffset) sourcePolygons.InsertNextCell(plg) poly = vtk.vtkPolyData() poly.SetPoints(sourcePoints) poly.SetVerts(sourceVertices) if faces: poly.SetPolys(sourcePolygons) clp = vtk.vtkCleanPolyData() vu.setInput(clp, poly) clp.PointMergingOn() clp.Update() return clp.GetOutput()
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 delaunay2D(plist, tol=None, c='gold', alpha=0.5, wire=False, bc=None, edges=False, legend=None, texture=None): ''' Create a mesh from points in the XY plane. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/delaunay2d.py) ''' src = vtk.vtkPointSource() src.SetNumberOfPoints(len(plist)) src.Update() pd = src.GetOutput() for i, p in enumerate(plist): pd.GetPoints().SetPoint(i, p) delny = vtk.vtkDelaunay2D() vu.setInput(delny, pd) if tol: delny.SetTolerance(tol) delny.Update() return vu.makeActor(delny.GetOutput(), c, alpha, wire, bc, edges, legend, texture)
def plane(pos=[0, 0, 0], normal=[0, 0, 1], sx=1, sy=None, c='g', bc='darkgreen', alpha=1, legend=None, texture=None): ''' Draw a plane of size sx and sy oriented perpendicular to vector normal and so that it passes through point pos. ''' if sy is None: sy = sx ps = vtk.vtkPlaneSource() ps.SetResolution(1, 1) tri = vtk.vtkTriangleFilter() tri.SetInputConnection(ps.GetOutputPort()) tri.Update() poly = tri.GetOutput() axis = np.array(normal) / np.linalg.norm(normal) theta = np.arccos(axis[2]) phi = np.arctan2(axis[1], axis[0]) t = vtk.vtkTransform() t.PostMultiply() t.Scale(sx, sy, 1) t.RotateY(theta * 57.3) t.RotateZ(phi * 57.3) tf = vtk.vtkTransformPolyDataFilter() vu.setInput(tf, poly) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() actor = vu.makeActor(pd, c=c, bc=bc, alpha=alpha, legend=legend, texture=texture) actor.SetPosition(pos) actor.PickableOff() return actor
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 grid(pos=[0, 0, 0], normal=[0, 0, 1], sx=1, sy=1, c='g', bc='darkgreen', lw=1, alpha=1, legend=None, resx=10, resy=10): '''Return a grid plane''' ps = vtk.vtkPlaneSource() ps.SetResolution(resx, resy) ps.Update() poly0 = ps.GetOutput() t0 = vtk.vtkTransform() t0.Scale(sx, sy, 1) tf0 = vtk.vtkTransformPolyDataFilter() vu.setInput(tf0, poly0) tf0.SetTransform(t0) tf0.Update() poly = tf0.GetOutput() axis = np.array(normal) / np.linalg.norm(normal) theta = np.arccos(axis[2]) phi = np.arctan2(axis[1], axis[0]) t = vtk.vtkTransform() t.PostMultiply() t.RotateY(theta * 57.3) t.RotateZ(phi * 57.3) tf = vtk.vtkTransformPolyDataFilter() vu.setInput(tf, poly) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() actor = vu.makeActor(pd, c=c, bc=bc, alpha=alpha, legend=legend) actor.GetProperty().SetRepresentationToWireframe() actor.GetProperty().SetLineWidth(lw) actor.SetPosition(pos) actor.PickableOff() return actor
def loadPoly(filename): '''Return a vtkPolyData object, NOT a vtkActor''' if not os.path.exists(filename): vc.printc(('Error in loadPoly: Cannot find', filename), c=1) return None fl = filename.lower() if '.vtk' in fl: reader = vtk.vtkPolyDataReader() elif '.ply' in fl: reader = vtk.vtkPLYReader() elif '.obj' in fl: reader = vtk.vtkOBJReader() elif '.stl' in fl: reader = vtk.vtkSTLReader() elif '.byu' in fl or '.g' in fl: reader = vtk.vtkBYUReader() elif '.vtp' in fl: reader = vtk.vtkXMLPolyDataReader() elif '.vts' in fl: reader = vtk.vtkXMLStructuredGridReader() elif '.vtu' in fl: reader = vtk.vtkXMLUnstructuredGridReader() elif '.txt' in fl: reader = vtk.vtkParticleReader() # (x y z scalar) elif '.xyz' in fl: reader = vtk.vtkParticleReader() else: reader = vtk.vtkDataReader() reader.SetFileName(filename) reader.Update() if '.vts' in fl: # structured grid gf = vtk.vtkStructuredGridGeometryFilter() gf.SetInputConnection(reader.GetOutputPort()) gf.Update() poly = gf.GetOutput() elif '.vtu' in fl: # unstructured grid gf = vtk.vtkGeometryFilter() gf.SetInputConnection(reader.GetOutputPort()) gf.Update() poly = gf.GetOutput() else: poly = reader.GetOutput() if not poly: vc.printc(('Unable to load', filename), c=1) return False cleanpd = vtk.vtkCleanPolyData() vu.setInput(cleanpd, poly) cleanpd.Update() return cleanpd.GetOutput()
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 disc(pos=[0, 0, 0], normal=[0, 0, 1], r1=0.5, r2=1, c='coral', bc='darkgreen', lw=1, alpha=1, legend=None, texture=None, res=12): '''Build a 2D disc of internal radius r1 and outer radius r2, oriented perpendicular to normal''' ps = vtk.vtkDiskSource() ps.SetInnerRadius(r1) ps.SetOuterRadius(r2) ps.SetRadialResolution(res) ps.SetCircumferentialResolution(res * 4) ps.Update() tr = vtk.vtkTriangleFilter() vu.setInput(tr, ps.GetOutputPort()) tr.Update() axis = np.array(normal) / np.linalg.norm(normal) theta = np.arccos(axis[2]) phi = np.arctan2(axis[1], axis[0]) t = vtk.vtkTransform() t.PostMultiply() t.RotateY(theta * 57.3) t.RotateZ(phi * 57.3) tf = vtk.vtkTransformPolyDataFilter() vu.setInput(tf, tr.GetOutput()) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() mapper = vtk.vtkPolyDataMapper() vu.setInput(mapper, pd) actor = vtk.vtkActor() actor.SetMapper(mapper) actor.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 actor.GetProperty().SetOpacity(alpha) actor.GetProperty().SetLineWidth(lw) actor.GetProperty().SetInterpolationToFlat() if bc: # defines a specific color for the backface backProp = vtk.vtkProperty() backProp.SetDiffuseColor(vc.getColor(bc)) backProp.SetOpacity(alpha) actor.SetBackfaceProperty(backProp) if texture: vu.assignTexture(actor, texture) vu.assignPhysicsMethods(actor) vu.assignConvenienceMethods(actor, legend) actor.SetPosition(pos) return actor