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: vio.printc("Error in ellipsoid(): scipy not installed. Skip.", 1) return None 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 spline(points, smooth=0.5, degree=2, s=2, c='b', alpha=1., nodes=False, legend=None, res=20): ''' Return a vtkActor for a spline that doesnt necessarly pass exactly throught all points. smooth = smoothing factor: 0 = interpolate points exactly, 1 = average point positions degree = degree of the spline (1<degree<5) nodes = True shows also original the points ''' try: from scipy.interpolate import splprep, splev except ImportError: vio.printc( 'Warning: ..scipy not installed, using vtkCardinalSpline instead.', 5) return _vtkspline(points, s, c, alpha, nodes, legend, res) Nout = len(points) * res # Number of points on the spline points = np.array(points) minx, miny, minz = np.min(points, axis=0) maxx, maxy, maxz = np.max(points, axis=0) maxb = max(maxx - minx, maxy - miny, maxz - minz) smooth *= maxb / 2 # must be in absolute units x, y, z = points[:, 0], points[:, 1], points[:, 2] tckp, _ = splprep([x, y, z], task=0, s=smooth, k=degree) # find the knots # evaluate spline, including interpolated points: xnew, ynew, znew = splev(np.linspace(0, 1, Nout), tckp) ppoints = vtk.vtkPoints() # Generate the polyline for the spline profileData = vtk.vtkPolyData() for i in range(Nout): ppoints.InsertPoint(i, xnew[i], ynew[i], znew[i]) lines = vtk.vtkCellArray() # Create the polyline lines.InsertNextCell(Nout) for i in range(Nout): lines.InsertCellPoint(i) profileData.SetPoints(ppoints) profileData.SetLines(lines) actline = vu.makeActor(profileData, c=c, alpha=alpha, legend=legend) actline.GetProperty().SetLineWidth(s) if nodes: actnodes = vs.points(points, r=s * 1.5, c=c, alpha=alpha) ass = vu.makeAssembly([actline, actnodes], legend=legend) return ass else: return actline
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 _vtkspline(points, s, c, alpha, nodes, legend, res): numberOfOutputPoints = len(points) * res # Number of points on the spline numberOfInputPoints = len(points) # One spline for each direction. aSplineX = vtk.vtkCardinalSpline() # interpolate the x values aSplineY = vtk.vtkCardinalSpline() # interpolate the y values aSplineZ = vtk.vtkCardinalSpline() # interpolate the z values inputPoints = vtk.vtkPoints() for i in range(0, numberOfInputPoints): x = points[i][0] y = points[i][1] z = points[i][2] aSplineX.AddPoint(i, x) aSplineY.AddPoint(i, y) aSplineZ.AddPoint(i, z) inputPoints.InsertPoint(i, x, y, z) inputData = vtk.vtkPolyData() inputData.SetPoints(inputPoints) points = vtk.vtkPoints() profileData = vtk.vtkPolyData() for i in range(0, numberOfOutputPoints): t = (numberOfInputPoints - 1.) / (numberOfOutputPoints - 1.) * i x, y, z = aSplineX.Evaluate(t), aSplineY.Evaluate( t), aSplineZ.Evaluate(t) points.InsertPoint(i, x, y, z) lines = vtk.vtkCellArray() # Create the polyline. lines.InsertNextCell(numberOfOutputPoints) for i in range(0, numberOfOutputPoints): lines.InsertCellPoint(i) profileData.SetPoints(points) profileData.SetLines(lines) actline = vu.makeActor(profileData, c=c, alpha=alpha, legend=legend) actline.GetProperty().SetLineWidth(s) actline.GetProperty().SetInterpolationToPhong() if nodes: pts = vu.coordinates(inputData) actnodes = vs.points(pts, r=s * 1.5, c=c, alpha=alpha) ass = vu.makeAssembly([actline, actnodes], legend=legend) return ass else: return actline
def cutPlane(actor, origin=(0, 0, 0), normal=(1, 0, 0), showcut=True): ''' Takes actor and cuts it with the plane defined by a point and a normal. showcut = shows the cut away part as thin wireframe showline = marks with a thick line the cut ''' plane = vtk.vtkPlane() plane.SetOrigin(origin) plane.SetNormal(normal) poly = vu.polydata(actor) clipper = vtk.vtkClipPolyData() vu.setInput(clipper, poly) clipper.SetClipFunction(plane) clipper.GenerateClippedOutputOn() clipper.SetValue(0.) clipper.Update() if hasattr(actor, 'GetProperty'): alpha = actor.GetProperty().GetOpacity() c = actor.GetProperty().GetColor() bf = actor.GetBackfaceProperty() else: alpha = 1 c = 'gold' bf = None leg = None if hasattr(actor, 'legend'): leg = actor.legend clipActor = vu.makeActor(clipper.GetOutput(), c=c, alpha=alpha, legend=leg) clipActor.SetBackfaceProperty(bf) acts = [clipActor] if showcut: cpoly = clipper.GetClippedOutput() restActor = vu.makeActor(cpoly, c=c, alpha=0.05, wire=1) acts.append(restActor) if len(acts) > 1: asse = vu.makeAssembly(acts) return asse else: return clipActor
def smoothMLS(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. 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 ''' 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: vio.printc('Please choose a higher fraction than' + str(f), 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
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. Examples: vp = plotter.vtkPlotter() vp.fxy('sin(3*x)*log(x-y)/3') or def z(x,y): return math.sin(x*y) vp.fxy(z) # or equivalently: vp.fxy(lambda x,y: math.sin(x*y)) ''' 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: vio.printc('Syntax Error in fxy()', 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(): vio.printc('Function is not real in the domain', 1) return vtk.vtkActor() if zlimits[0]: a = cutPlane(poly, (0, 0, zlimits[0]), (0, 0, 1), False) poly = vu.polydata(a) if zlimits[1]: a = cutPlane(poly, (0, 0, zlimits[1]), (0, 0, -1), False) 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