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 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() tf.SetInputConnection(ps.GetOutputPort()) tf.Update() mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(tf.GetOutputPort()) if followcam: # follow cam actor = vtk.vtkFollower() actor.SetCamera(camera) if not camera: colors.printc('Warning: vtkCamera does not yet exist for polygon', c=5) else: actor = Actor() # vtk.vtkActor() actor.SetMapper(mapper) actor.GetProperty().SetColor(colors.getColor(c)) # check if color string contains a float, in this case ignore alpha al = colors.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(colors.getColor(bc)) backProp.SetOpacity(alpha) actor.SetBackfaceProperty(backProp) if texture: actor.texture(texture) actor.SetPosition(pos) 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 = actor.polydata() maskPts.SetInputData(src) arrow = vtk.vtkLineSource() arrow.SetPoint1(0,0,0) arrow.SetPoint2(.75,0,0) 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) glyphActor.PickableOff() aactor = Assembly([actor, glyphActor], legend=legend) return aactor
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 ''' 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 _colorPoints(plist, cols, r, alpha, legend): if len(plist) > len(cols): vio.printc(("Mismatch in colorPoints()", len(plist), len(cols)), 1) exit() if len(plist) != len(cols): vio.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 makeActor(poly, c='gold', alpha=0.5, wire=False, bc=None, edges=False, legend=None, texture=None): ''' Return a vtkActor from an input vtkPolyData, optional args: c, color in RGB format, hex, symbol or name alpha, transparency (0=invisible) wire, show surface as wireframe bc, backface color of internal surface edges, show edges as line on top of surface legend optional string texture jpg file name of surface texture, eg. 'metalfloor1' ''' clp = vtk.vtkCleanPolyData() setInput(clp, poly) clp.Update() pdnorm = vtk.vtkPolyDataNormals() setInput(pdnorm, clp.GetOutput()) pdnorm.ComputePointNormalsOn() pdnorm.ComputeCellNormalsOn() pdnorm.FlipNormalsOff() pdnorm.ConsistencyOn() pdnorm.Update() mapper = vtk.vtkPolyDataMapper() # check if color string contains a float, in this case ignore alpha if alpha is None: alpha = 0.5 al = colors.getAlpha(c) if al: alpha = al setInput(mapper, pdnorm.GetOutput()) actor = vtk.vtkActor() actor.SetMapper(mapper) prp = actor.GetProperty() ######################################################################### ### On some vtk versions/platforms points are redered as ugly squares ### in such a case uncomment this line: if vtk.vtkVersion().GetVTKMajorVersion() > 6: prp.RenderPointsAsSpheresOn() ######################################################################### if c is None: mapper.ScalarVisibilityOn() else: mapper.ScalarVisibilityOff() c = colors.getColor(c) prp.SetColor(c) prp.SetOpacity(alpha) prp.SetSpecular(0.1) prp.SetSpecularColor(c) prp.SetSpecularPower(1) prp.SetAmbient(0.1) prp.SetAmbientColor(c) prp.SetDiffuse(1) prp.SetDiffuseColor(c) if edges: prp.EdgeVisibilityOn() if wire: prp.SetRepresentationToWireframe() if texture: mapper.ScalarVisibilityOff() assignTexture(actor, texture) if bc: # defines a specific color for the backface backProp = vtk.vtkProperty() backProp.SetDiffuseColor(colors.getColor(bc)) backProp.SetOpacity(alpha) actor.SetBackfaceProperty(backProp) assignPhysicsMethods(actor) assignConvenienceMethods(actor, legend) 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 = polydata(obj) planes = vtk.vtkPlanes() planes.SetBounds(apd.GetBounds()) clipper = vtk.vtkClipPolyData() setInput(clipper, apd) clipper.SetClipFunction(planes) clipper.InsideOutOn() clipper.GenerateClippedOutputOn() # check if color string contains a float, in this case ignore alpha al = colors.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(colors.getColor(c)) act0.GetProperty().SetOpacity(alpha) backProp = vtk.vtkProperty() backProp.SetDiffuseColor(colors.getColor(bc)) backProp.SetOpacity(alpha) act0.SetBackfaceProperty(backProp) act0.GetProperty().SetInterpolationToFlat() assignPhysicsMethods(act0) 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(colors.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) setInput(boxWidget, apd) boxWidget.PlaceWidget() boxWidget.AddObserver("InteractionEvent", SelectPolygons) boxWidget.On() colors.printc('\nCutterWidget:\n Move handles to cut parts of the actor', 'm') colors.printc(' Press q to continue, Escape to exit', 'm') colors.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() setInput(confilter, clipper.GetOutput()) confilter.SetExtractionModeToLargestRegion() confilter.Update() cpd = vtk.vtkCleanPolyData() setInput(cpd, confilter.GetOutput()) cpd.Update() w = vtk.vtkPolyDataWriter() setInput(w, cpd.GetOutput()) w.SetFileName(outputname) w.Write() colors.printc("Saved file: " + outputname, 'g') elif key == "Escape": exit(0) iren.Initialize() iren.AddObserver("KeyPressEvent", cwkeypress) iren.Start() boxWidget.Off() return act0
def text(txt, pos=(0, 0, 0), normal=(0, 0, 1), s=1, depth=0.1, c='k', alpha=1, bc=None, texture=None, followcam=False, cam=None): ''' Returns a vtkActor that shows a text in 3D. pos = position in 3D space if an integer is passed [1 -> 8], places text in a corner s = size of text depth = text thickness followcam = True, the text will auto-orient itself to it ''' if isinstance(pos, int): cornerAnnotation = vtk.vtkCornerAnnotation() cornerAnnotation.SetNonlinearFontScaleFactor(s / 3) cornerAnnotation.SetText(pos - 1, txt) cornerAnnotation.GetTextProperty().SetColor(vc.getColor(c)) return cornerAnnotation tt = vtk.vtkVectorText() tt.SetText(txt) tt.Update() ttmapper = vtk.vtkPolyDataMapper() if depth: extrude = vtk.vtkLinearExtrusionFilter() extrude.SetInputConnection(tt.GetOutputPort()) extrude.SetExtrusionTypeToVectorExtrusion() extrude.SetVector(0, 0, 1) extrude.SetScaleFactor(depth) ttmapper.SetInputConnection(extrude.GetOutputPort()) else: ttmapper.SetInputConnection(tt.GetOutputPort()) if followcam: #follow cam ttactor = vtk.vtkFollower() ttactor.SetCamera(cam) else: ttactor = vtk.vtkActor() ttactor.SetMapper(ttmapper) ttactor.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 ttactor.GetProperty().SetOpacity(alpha) nax = np.linalg.norm(normal) if nax: normal = np.array(normal) / nax theta = np.arccos(normal[2]) phi = np.arctan2(normal[1], normal[0]) ttactor.SetScale(s, s, s) ttactor.RotateZ(phi * 57.3) ttactor.RotateY(theta * 57.3) ttactor.SetPosition(pos) if bc: # defines a specific color for the backface backProp = vtk.vtkProperty() backProp.SetDiffuseColor(vc.getColor(bc)) backProp.SetOpacity(alpha) ttactor.SetBackfaceProperty(backProp) if texture: vu.assignTexture(ttactor, texture) vu.assignConvenienceMethods(ttactor, None) vu.assignPhysicsMethods(ttactor) return ttactor
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): vio.printc(("Mismatch in spheres() colors", len(centers), len(c)), 1) exit() if len(centers) != len(c): vio.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): vio.printc(("Mismatch in spheres() radius", len(centers), len(r)), 1) exit() if len(centers) != len(r): vio.printc(("Warning: mismatch in spheres() radius", len(centers), len(r))) if cisseq and risseq: vio.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 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. [**Example**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/manyspheres.py) ![manysph](https://user-images.githubusercontent.com/32848391/46818673-1f566b80-cd82-11e8-9a61-be6a56160f1c.png) ''' cisseq = False if utils.isSequence(c): cisseq = True if cisseq: if len(centers) > len(c): colors.printc("Mismatch in spheres() colors", len(centers), len(c), c=1) exit() if len(centers) != len(c): colors.printc("Warning: mismatch in spheres() colors", len(centers), len(c)) risseq = False if utils.isSequence(r): risseq = True if risseq: if len(centers) > len(r): colors.printc("Mismatch in spheres() radius", len(centers), len(r), c=1) exit() if len(centers) != len(r): colors.printc("Warning: mismatch in spheres() radius", len(centers), len(r)) if cisseq and risseq: colors.printc("Limitation: c and r cannot be both sequences.", c=1) exit() src = vtk.vtkSphereSource() if not risseq: src.SetRadius(r) src.SetPhiResolution(res) src.SetThetaResolution(2 * 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(colors.getColor(c[i])) * 255 ucols.InsertNextTuple3(cc[0], cc[1], cc[2]) 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) glyph.SetInputData(pd) glyph.Update() mapper = vtk.vtkPolyDataMapper() mapper.SetInputData(glyph.GetOutput()) if cisseq: mapper.ScalarVisibilityOn() else: mapper.ScalarVisibilityOff() actor = Actor() #vtk.vtkActor() actor.SetMapper(mapper) actor.GetProperty().SetInterpolationToPhong() # check if color string contains a float, in this case ignore alpha al = colors.getAlpha(c) if al: alpha = al actor.GetProperty().SetOpacity(alpha) if not cisseq: if texture is not None: actor.texture(texture) mapper.ScalarVisibilityOff() else: actor.GetProperty().SetColor(colors.getColor(c)) return actor
def text(txt, pos=(0, 0, 0), normal=(0, 0, 1), s=1, depth=0.1, c='k', alpha=1, bc=None, texture=None, followcam=False, cam=None): ''' Returns a vtkActor that shows a text in 3D. Options: pos = position in 3D space, if an integer is passed [1 -> 8], places text in one of the 4 corners s = size of text depth = text thickness followcam = False, if True the text will auto-orient itself to it. [**Example1**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/colorcubes.py) [**Example2**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/mesh_coloring.py) ''' if isinstance(pos, int): cornerAnnotation = vtk.vtkCornerAnnotation() cornerAnnotation.SetNonlinearFontScaleFactor(s / 3) cornerAnnotation.SetText(pos - 1, str(txt)) cornerAnnotation.GetTextProperty().SetColor(colors.getColor(c)) return cornerAnnotation tt = vtk.vtkVectorText() tt.SetText(str(txt)) tt.Update() ttmapper = vtk.vtkPolyDataMapper() if followcam: depth = 0 normal = (0, 0, 1) if depth: extrude = vtk.vtkLinearExtrusionFilter() extrude.SetInputConnection(tt.GetOutputPort()) extrude.SetExtrusionTypeToVectorExtrusion() extrude.SetVector(0, 0, 1) extrude.SetScaleFactor(depth) ttmapper.SetInputConnection(extrude.GetOutputPort()) else: ttmapper.SetInputConnection(tt.GetOutputPort()) if followcam: ttactor = vtk.vtkFollower() ttactor.SetCamera(cam) else: ttactor = Actor() ttactor.SetMapper(ttmapper) ttactor.GetProperty().SetColor(colors.getColor(c)) # check if color string contains a float, in this case ignore alpha al = colors.getAlpha(c) if al: alpha = al ttactor.GetProperty().SetOpacity(alpha) nax = np.linalg.norm(normal) if nax: normal = np.array(normal) / nax theta = np.arccos(normal[2]) phi = np.arctan2(normal[1], normal[0]) ttactor.SetScale(s, s, s) ttactor.RotateZ(phi * 57.3) ttactor.RotateY(theta * 57.3) ttactor.SetPosition(pos) if bc: # defines a specific color for the backface backProp = vtk.vtkProperty() backProp.SetDiffuseColor(colors.getColor(bc)) backProp.SetOpacity(alpha) ttactor.SetBackfaceProperty(backProp) if texture: ttactor.texture(texture) return ttactor