def subdivide(actor, N=1, method=0, legend=None): ''' Increase the number of points in actor surface N = number of subdivisions method = 0, Loop method = 1, Linear method = 2, Adaptive method = 3, Butterfly ''' triangles = vtk.vtkTriangleFilter() vu.setInput(triangles, vu.polydata(actor)) triangles.Update() originalMesh = triangles.GetOutput() if method == 0: sdf = vtk.vtkLoopSubdivisionFilter() elif method == 1: sdf = vtk.vtkLinearSubdivisionFilter() elif method == 2: sdf = vtk.vtkAdaptiveSubdivisionFilter() elif method == 3: sdf = vtk.vtkButterflySubdivisionFilter() else: vio.printc('Error in subdivide: unknown method.', 'r') exit(1) if method != 2: sdf.SetNumberOfSubdivisions(N) vu.setInput(sdf, originalMesh) sdf.Update() out = sdf.GetOutput() if legend is None and hasattr(actor, 'legend'): legend = actor.legend sactor = vu.makeActor(out, legend=legend) sactor.GetProperty().SetOpacity(actor.GetProperty().GetOpacity()) sactor.GetProperty().SetColor(actor.GetProperty().GetColor()) sactor.GetProperty().SetRepresentation( actor.GetProperty().GetRepresentation()) return sactor
def decimate(actor, fraction=0.5, N=None, verbose=True, boundaries=True): ''' Downsample the number of vertices in a mesh. fraction gives the desired target of reduction. E.g. fraction=0.1 leaves 10% of the original nr of vertices. ''' poly = vu.polydata(actor, True) if N: # N = desired number of points Np = poly.GetNumberOfPoints() fraction = float(N) / Np if fraction >= 1: return actor decimate = vtk.vtkDecimatePro() vu.setInput(decimate, poly) decimate.SetTargetReduction(1. - fraction) decimate.PreserveTopologyOff() if boundaries: decimate.BoundaryVertexDeletionOn() else: decimate.BoundaryVertexDeletionOff() decimate.Update() if verbose: print('Input nr. of pts:', poly.GetNumberOfPoints(), end='') print(' output:', decimate.GetOutput().GetNumberOfPoints()) mapper = actor.GetMapper() vu.setInput(mapper, decimate.GetOutput()) mapper.Update() actor.Modified() if hasattr(actor, 'poly'): actor.poly = decimate.GetOutput() return actor # return same obj for concatenation
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 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 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: print('Recalculating normals for', N, 'points') normals = vtk.vtkPCANormalEstimation() vu.setInput(normals, polyData) normals.SetSampleSize(int(N / 50)) normals.SetNormalOrientationToGraphTraversal() distance.SetInputConnection(normals.GetOutputPort()) 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 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: vu.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 cwkeypress(obj, event): key = obj.GetKeySym() if key == "q" or key == "space" or key == "Return": iren.ExitCallback() elif key == "X": confilter = vtk.vtkPolyDataConnectivityFilter() vu.setInput(confilter, clipper.GetOutput()) confilter.SetExtractionModeToLargestRegion() confilter.Update() cpd = vtk.vtkCleanPolyData() vu.setInput(cpd, confilter.GetOutput()) cpd.Update() vio.write(cpd.GetOutput(), outputname) elif key == "Escape": exit(0)
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 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.makePolyData(pts) 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 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 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
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 _colorPoints(plist, cols, r, alpha, legend): if len(plist) > len(cols): vu.printc(("Mismatch in colorPoints()", len(plist), len(cols)), 1) exit() if len(plist) != len(cols): vu.printc( ("Warning: mismatch in colorPoints()", len(plist), len(cols))) src = vtk.vtkPointSource() src.SetNumberOfPoints(len(plist)) src.Update() vertexFilter = vtk.vtkVertexGlyphFilter() vu.setInput(vertexFilter, src.GetOutput()) vertexFilter.Update() pd = vertexFilter.GetOutput() ucols = vtk.vtkUnsignedCharArray() ucols.SetNumberOfComponents(3) ucols.SetName("RGB") for i, p in enumerate(plist): pd.GetPoints().SetPoint(i, p) c = np.array(vc.getColor(cols[i])) * 255 if vu.vtkMV: ucols.InsertNextTuple3(c[0], c[1], c[2]) else: ucols.InsertNextTupleValue(c) pd.GetPointData().SetScalars(ucols) mapper = vtk.vtkPolyDataMapper() vu.setInput(mapper, pd) mapper.ScalarVisibilityOn() actor = vtk.vtkActor() actor.SetMapper(mapper) actor.GetProperty().SetInterpolationToFlat() # 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().SetPointSize(r) 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 box(pos=[0, 0, 0], length=1, width=2, height=3, normal=(0, 0, 1), c='g', alpha=1, wire=False, legend=None, texture=None): '''Build a box of dimensions x=length, y=width and z=height oriented along vector normal''' src = vtk.vtkCubeSource() src.SetXLength(length) src.SetYLength(width) src.SetZLength(height) src.Update() poly = src.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, alpha, wire, legend=legend, texture=texture) actor.SetPosition(pos) return actor
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
def spheres(centers, r=1, c='r', alpha=1, wire=False, legend=None, texture=None, res=8): ''' Build a (possibly large) set of spheres at centers of radius r. Either c or r can be a list of RGB colors or radii. ''' cisseq = False if vu.isSequence(c): cisseq = True if cisseq: if len(centers) > len(c): vu.printc(("Mismatch in spheres() colors", len(centers), len(c)), 1) exit() if len(centers) != len(c): vu.printc(("Warning: mismatch in spheres() colors", len(centers), len(c))) risseq = False if vu.isSequence(r): risseq = True if risseq: if len(centers) > len(r): vu.printc(("Mismatch in spheres() radius", len(centers), len(r)), 1) exit() if len(centers) != len(r): vu.printc(("Warning: mismatch in spheres() radius", len(centers), len(r))) if cisseq and risseq: vu.printc("Limitation: c and r cannot be both sequences.", 1) exit() src = vtk.vtkSphereSource() if not risseq: src.SetRadius(r) src.SetPhiResolution(res) src.SetThetaResolution(res) src.Update() glyph = vtk.vtkGlyph3D() glyph.SetSourceConnection(src.GetOutputPort()) psrc = vtk.vtkPointSource() psrc.SetNumberOfPoints(len(centers)) psrc.Update() pd = psrc.GetOutput() vpts = pd.GetPoints() if cisseq: glyph.SetColorModeToColorByScalar() ucols = vtk.vtkUnsignedCharArray() ucols.SetNumberOfComponents(3) ucols.SetName("colors") for i, p in enumerate(centers): vpts.SetPoint(i, p) cc = np.array(vc.getColor(c[i])) * 255 if vu.vtkMV: ucols.InsertNextTuple3(cc[0], cc[1], cc[2]) else: ucols.InsertNextTupleValue(cc) pd.GetPointData().SetScalars(ucols) glyph.ScalingOff() elif risseq: glyph.SetScaleModeToScaleByScalar() urads = vtk.vtkFloatArray() urads.SetName("scales") for i, p in enumerate(centers): vpts.SetPoint(i, p) urads.InsertNextValue(r[i]) pd.GetPointData().SetScalars(urads) else: for i, p in enumerate(centers): vpts.SetPoint(i, p) vu.setInput(glyph, pd) glyph.Update() mapper = vtk.vtkPolyDataMapper() vu.setInput(mapper, glyph.GetOutput()) if cisseq: mapper.ScalarVisibilityOn() else: mapper.ScalarVisibilityOff() actor = vtk.vtkActor() actor.SetMapper(mapper) actor.GetProperty().SetInterpolationToPhong() # check if color string contains a float, in this case ignore alpha al = vc.getAlpha(c) if al: alpha = al actor.GetProperty().SetOpacity(alpha) if not cisseq: if texture is not None: vu.assignTexture(actor, texture) mapper.ScalarVisibilityOff() else: actor.GetProperty().SetColor(vc.getColor(c)) vu.assignConvenienceMethods(actor, legend) return actor
def cylinder(pos=[0, 0, 0], r=1, height=1, axis=[0, 0, 1], c='teal', wire=0, alpha=1, edges=False, legend=None, texture=None, res=24): ''' Build a cylinder of specified height and radius r, centered at pos. If pos is a list of 2 points, e.g. pos=[v1,v2], build a cylinder with base centered at v1 and top at v2. ''' if vu.isSequence(pos[0]): # assume user is passing pos=[base, top] base = np.array(pos[0]) top = np.array(pos[1]) pos = (base + top) / 2 height = np.linalg.norm(top - base) axis = top - base axis = vu.norm(axis) else: axis = vu.norm(axis) base = pos - axis * height / 2 top = pos + axis * height / 2 cyl = vtk.vtkCylinderSource() cyl.SetResolution(res) cyl.SetRadius(r) cyl.SetHeight(height) cyl.Update() theta = np.arccos(axis[2]) phi = np.arctan2(axis[1], axis[0]) t = vtk.vtkTransform() t.PostMultiply() t.RotateX(90) #put it along Z t.RotateY(theta * 57.3) t.RotateZ(phi * 57.3) tf = vtk.vtkTransformPolyDataFilter() vu.setInput(tf, cyl.GetOutput()) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() actor = vu.makeActor(pd, c, alpha, wire, edges=edges, legend=legend, texture=texture) actor.GetProperty().SetInterpolationToPhong() actor.SetPosition(pos) setattr(actor, 'base', base) setattr(actor, 'top', top) return actor
def cutterWidget(obj, outputname='clipped.vtk', c=(0.2, 0.2, 1), alpha=1, bc=(0.7, 0.8, 1), legend=None): '''Pop up a box widget to cut parts of actor. Return largest part.''' apd = vu.polydata(obj) planes = vtk.vtkPlanes() planes.SetBounds(apd.GetBounds()) clipper = vtk.vtkClipPolyData() vu.setInput(clipper, apd) clipper.SetClipFunction(planes) clipper.InsideOutOn() clipper.GenerateClippedOutputOn() # check if color string contains a float, in this case ignore alpha al = vc.getAlpha(c) if al: alpha = al act0Mapper = vtk.vtkPolyDataMapper() # the part which stays act0Mapper.SetInputConnection(clipper.GetOutputPort()) act0 = vtk.vtkActor() act0.SetMapper(act0Mapper) act0.GetProperty().SetColor(vc.getColor(c)) act0.GetProperty().SetOpacity(alpha) backProp = vtk.vtkProperty() backProp.SetDiffuseColor(vc.getColor(bc)) backProp.SetOpacity(alpha) act0.SetBackfaceProperty(backProp) #act0 = makeActor(clipper.GetOutputPort()) act0.GetProperty().SetInterpolationToFlat() vu.assignPhysicsMethods(act0) vu.assignConvenienceMethods(act0, legend) act1Mapper = vtk.vtkPolyDataMapper() # the part which is cut away act1Mapper.SetInputConnection(clipper.GetClippedOutputPort()) act1 = vtk.vtkActor() act1.SetMapper(act1Mapper) act1.GetProperty().SetColor(vc.getColor(c)) act1.GetProperty().SetOpacity(alpha / 10.) act1.GetProperty().SetRepresentationToWireframe() act1.VisibilityOn() ren = vtk.vtkRenderer() ren.SetBackground(1, 1, 1) ren.AddActor(act0) ren.AddActor(act1) renWin = vtk.vtkRenderWindow() renWin.AddRenderer(ren) renWin.SetSize(600, 700) iren = vtk.vtkRenderWindowInteractor() iren.SetRenderWindow(renWin) istyl = vtk.vtkInteractorStyleSwitch() istyl.SetCurrentStyleToTrackballCamera() iren.SetInteractorStyle(istyl) def SelectPolygons(vobj, event): vobj.GetPlanes(planes) boxWidget = vtk.vtkBoxWidget() boxWidget.OutlineCursorWiresOn() boxWidget.GetSelectedOutlineProperty().SetColor(1, 0, 1) boxWidget.GetOutlineProperty().SetColor(0.1, 0.1, 0.1) boxWidget.GetOutlineProperty().SetOpacity(0.8) boxWidget.SetPlaceFactor(1.05) boxWidget.SetInteractor(iren) vu.setInput(boxWidget, apd) boxWidget.PlaceWidget() boxWidget.AddObserver("InteractionEvent", SelectPolygons) boxWidget.On() vio.printc('\nCutterWidget:\n Move handles to cut parts of the actor', 'm') vio.printc(' Press q to continue, Escape to exit', 'm') vio.printc((" Press X to save file to", outputname), 'm') def cwkeypress(obj, event): key = obj.GetKeySym() if key == "q" or key == "space" or key == "Return": iren.ExitCallback() elif key == "X": confilter = vtk.vtkPolyDataConnectivityFilter() vu.setInput(confilter, clipper.GetOutput()) confilter.SetExtractionModeToLargestRegion() confilter.Update() cpd = vtk.vtkCleanPolyData() vu.setInput(cpd, confilter.GetOutput()) cpd.Update() vio.write(cpd.GetOutput(), outputname) elif key == "Escape": exit(0) iren.Initialize() iren.AddObserver("KeyPressEvent", cwkeypress) iren.Start() boxWidget.Off() return act0