def build_img_plt(formula, tfile): import matplotlib.pyplot as plt plt.rc('text', usetex=usetex) formula1 = '$' + formula + '$' plt.axis('off') col = colors.getColor(c) if bg: bx = dict(boxstyle="square", ec=col, fc=colors.getColor(bg)) else: bx = None plt.text(0.5, 0.5, formula1, size=res, color=col, alpha=alpha, ha="center", va="center", bbox=bx) plt.savefig('_lateximg.png', format='png', transparent=True, bbox_inches='tight', pad_inches=0) plt.close()
def build_img_plt(formula): buf = io.BytesIO() plt.rc('text', usetex=True) plt.axis('off') col = colors.getColor(c) if bg: bx = dict(boxstyle="square", ec=col, fc=colors.getColor(bg)) else: bx = None plt.text(0.5, 0.5, f'${formula}$', size=res, color=col, alpha=alpha, ha="center", va="center", bbox=bx) plt.savefig(buf, format='png', transparent=True, bbox_inches='tight', pad_inches=0) plt.close() im = Image.open(buf) return im
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 Disc( pos=(0, 0, 0), normal=(0, 0, 1), r1=0.5, r2=1, c="coral", bc="darkgreen", lw=1, alpha=1, res=12, resphi=None, ): """ Build a 2D disc of internal radius `r1` and outer radius `r2`, oriented perpendicular to `normal`. |Disk| """ ps = vtk.vtkDiskSource() ps.SetInnerRadius(r1) ps.SetOuterRadius(r2) ps.SetRadialResolution(res) if not resphi: resphi = 6 * res ps.SetCircumferentialResolution(resphi) ps.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(np.rad2deg(theta)) t.RotateZ(np.rad2deg(phi)) tf = vtk.vtkTransformPolyDataFilter() tf.SetInputData(ps.GetOutput()) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() mapper = vtk.vtkPolyDataMapper() mapper.SetInputData(pd) actor = Actor() # vtk.vtkActor() actor.SetMapper(mapper) actor.GetProperty().SetColor(colors.getColor(c)) 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) actor.SetPosition(pos) settings.collectable_actors.append(actor) 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 Polygon(pos=(0, 0, 0), normal=(0, 0, 1), nsides=6, r=1, c="coral", bc="darkgreen", lw=1, alpha=1, followcam=False): """ Build a 2D polygon of `nsides` of radius `r` oriented as `normal`. :param followcam: if `True` the text will auto-orient itself to the active camera. A ``vtkCamera`` object can also be passed. :type followcam: bool, vtkCamera |Polygon| """ 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: import vtkplotter.plotter as plt actor = vtk.vtkFollower() if isinstance(followcam, vtk.vtkCamera): actor.SetCamera(followcam) else: actor.SetCamera(plt._plotter_instance.camera) else: actor = Actor() actor.SetMapper(mapper) actor.GetProperty().SetColor(colors.getColor(c)) 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) actor.SetPosition(pos) return actor
def _colorPoints(plist, cols, r, alpha, legend): n = len(plist) if n > len(cols): colors.printc("Mismatch in colorPoints()", n, len(cols), c=1) exit() if n != len(cols): colors.printc("Warning: mismatch in colorPoints()", n, len(cols)) src = vtk.vtkPointSource() src.SetNumberOfPoints(n) src.Update() vertexFilter = vtk.vtkVertexGlyphFilter() vertexFilter.SetInputData(src.GetOutput()) vertexFilter.Update() pd = vertexFilter.GetOutput() ucols = vtk.vtkUnsignedCharArray() ucols.SetNumberOfComponents(3) ucols.SetName("RGB") for i, p in enumerate(plist): c = np.array(colors.getColor(cols[i])) * 255 ucols.InsertNextTuple3(c[0], c[1], c[2]) pd.GetPoints().SetData(numpy_to_vtk(plist, deep=True)) pd.GetPointData().SetScalars(ucols) mapper = vtk.vtkPolyDataMapper() mapper.SetInputData(pd) mapper.ScalarVisibilityOn() actor = Actor() #vtk.vtkActor() actor.SetMapper(mapper) actor.GetProperty().SetInterpolationToFlat() actor.GetProperty().SetOpacity(alpha) actor.GetProperty().SetPointSize(r) return actor
def lighting(self, style='', ambient=None, diffuse=None, specular=None, specularPower=None, specularColor=None, enabled=True): """ Set the ambient, diffuse, specular and specularPower lighting constants. :param str,int style: preset style, can be `[metallic, plastic, shiny, glossy, ambient]` :param float ambient: ambient fraction of emission [0-1] :param float diffuse: emission of diffused light in fraction [0-1] :param float specular: fraction of reflected light [0-1] :param float specularPower: precision of reflection [1-100] :param color specularColor: color that is being reflected by the surface :param bool enabled: enable/disable all surface light emission |wikiphong| |specular| |specular.py|_ """ pr = self.GetProperty() if style: if hasattr(pr, "GetColor"): # could be Volume c = pr.GetColor() else: c = (1, 1, 0.99) mpr = self._mapper if hasattr(mpr, 'GetScalarVisibility') and mpr.GetScalarVisibility(): c = (1, 1, 0.99) if style == 'metallic': pars = [0.1, 0.3, 1.0, 10, c] elif style == 'plastic': pars = [0.3, 0.4, 0.3, 5, c] elif style == 'shiny': pars = [0.2, 0.6, 0.8, 50, c] elif style == 'glossy': pars = [0.1, 0.7, 0.9, 90, (1, 1, 0.99)] elif style == 'ambient': pars = [1.0, 0.0, 0.0, 0, (1, 1, 1)] elif style == 'default': pars = [0.1, 1.0, 0.05, 5, c] else: colors.printc("Error in lighting(): Available styles are", c=1) colors.printc( " [default, metallic, plastic, shiny, glossy, ambient]", c=1) raise RuntimeError() pr.SetAmbient(pars[0]) pr.SetDiffuse(pars[1]) pr.SetSpecular(pars[2]) pr.SetSpecularPower(pars[3]) if hasattr(pr, "GetColor"): pr.SetSpecularColor(pars[4]) if ambient is not None: pr.SetAmbient(ambient) if diffuse is not None: pr.SetDiffuse(diffuse) if specular is not None: pr.SetSpecular(specular) if specularPower is not None: pr.SetSpecularPower(specularPower) if specularColor is not None: pr.SetSpecularColor(colors.getColor(specularColor)) if not enabled: pr.LightingOff() return self
def _colorPoints(plist, cols, r, alpha): n = len(plist) if n > len(cols): colors.printc("~times Error: mismatch in colorPoints()", n, len(cols), c=1) exit() if n != len(cols): colors.printc("~lightning Warning: mismatch in colorPoints()", n, len(cols)) src = vtk.vtkPointSource() src.SetNumberOfPoints(n) src.Update() vgf = vtk.vtkVertexGlyphFilter() vgf.SetInputData(src.GetOutput()) vgf.Update() pd = vgf.GetOutput() ucols = vtk.vtkUnsignedCharArray() ucols.SetNumberOfComponents(3) ucols.SetName("pointsRGB") for i in range(len(plist)): c = np.array(colors.getColor(cols[i])) * 255 ucols.InsertNextTuple3(c[0], c[1], c[2]) pd.GetPoints().SetData(numpy_to_vtk(plist, deep=True)) pd.GetPointData().SetScalars(ucols) actor = Actor(pd, c, alpha) actor.mapper.ScalarVisibilityOn() actor.GetProperty().SetInterpolationToFlat() actor.GetProperty().SetPointSize(r) settings.collectable_actors.append(actor) return actor
def changeBackColor(self, c, acts=None, t=None, duration=None): """Gradually change backface color for the input list of actors. An initial backface color should be set in advance.""" if self.bookingMode: acts, t, duration, rng = self._parse(acts, t, duration) col2 = getColor(c) for tt in rng: inputvalues = [] for a in acts: if a.GetBackfaceProperty(): col1 = a.backColor() r = linInterpolate(tt, [t, t + duration], [col1[0], col2[0]]) g = linInterpolate(tt, [t, t + duration], [col1[1], col2[1]]) b = linInterpolate(tt, [t, t + duration], [col1[2], col2[2]]) inputvalues.append((r, g, b)) else: inputvalues.append(None) self.events.append( (tt, self.changeBackColor, acts, inputvalues)) else: for i, a in enumerate(self._performers): a.backColor(self._inputvalues[i]) return self
def addFrame(c=None, alpha=0.5, bg=None, lw=0.5): if c is None: # automatic black or white c = (0.9, 0.9, 0.9) if numpy.sum(settings.plotter_instance.renderer.GetBackground()) > 1.5: c = (0.1, 0.1, 0.1) c = colors.getColor(c) ppoints = vtk.vtkPoints() # Generate the polyline psqr = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]] for i, pt in enumerate(psqr): ppoints.InsertPoint(i, pt[0], pt[1], 0) lines = vtk.vtkCellArray() lines.InsertNextCell(len(psqr)) for i in range(len(psqr)): lines.InsertCellPoint(i) pd = vtk.vtkPolyData() pd.SetPoints(ppoints) pd.SetLines(lines) mapper = vtk.vtkPolyDataMapper2D() mapper.SetInputData(pd) cs = vtk.vtkCoordinate() cs.SetCoordinateSystemToNormalizedViewport() mapper.SetTransformCoordinate(cs) fractor = vtk.vtkActor2D() fractor.GetPositionCoordinate().SetValue(0, 0) fractor.GetPosition2Coordinate().SetValue(1, 1) fractor.SetMapper(mapper) fractor.GetProperty().SetColor(c) fractor.GetProperty().SetOpacity(alpha) fractor.GetProperty().SetLineWidth(lw) settings.plotter_instance.renderer.AddActor(fractor)
def color(self, col): """Assign a color or a set of colors to a volume along the range of the scalar value. A single constant color can also be assigned. Any matplotlib color map name is also accepted, e.g. ``volume.color('jet')``. E.g.: say that your voxel scalar runs from -3 to 6, and you want -3 to show red and 1.5 violet and 6 green, then just set: ``volume.color(['red', 'violet', 'green'])`` """ smin, smax = self._imagedata.GetScalarRange() volumeProperty = self.GetProperty() ctf = vtk.vtkColorTransferFunction() self._color = col if utils.isSequence(col): for i, ci in enumerate(col): r, g, b = colors.getColor(ci) xalpha = smin + (smax - smin) * i / (len(col) - 1) ctf.AddRGBPoint(xalpha, r, g, b) #colors.printc('\tcolor at', round(xalpha, 1), # '\tset to', colors.getColorName((r, g, b)), c='b', bold=0) elif isinstance(col, str): if col in colors.colors.keys() or col in colors.color_nicks.keys(): r, g, b = colors.getColor(col) ctf.AddRGBPoint(smin, r, g, b) # constant color ctf.AddRGBPoint(smax, r, g, b) elif colors._mapscales: for x in np.linspace(smin, smax, num=64, endpoint=True): r, g, b = colors.colorMap(x, name=col, vmin=smin, vmax=smax) ctf.AddRGBPoint(x, r, g, b) elif isinstance(col, int): r, g, b = colors.getColor(col) ctf.AddRGBPoint(smin, r, g, b) # constant color ctf.AddRGBPoint(smax, r, g, b) else: colors.printc("volume.color(): unknown input type:", col, c=1) volumeProperty.SetColor(ctf) volumeProperty.SetInterpolationTypeToLinear() #volumeProperty.SetInterpolationTypeToNearest() return self
def xyplot(points, title='', c='b', corner=1, lines=False): """ Return a vtkActor that is a plot of 2D points in x and y. Use corner to assign its position: 1=topleft, 2=topright, 3=bottomleft, 4=bottomright. """ c = vc.getColor(c) # allow different codings array_x = vtk.vtkFloatArray() array_y = vtk.vtkFloatArray() array_x.SetNumberOfTuples(len(points)) array_y.SetNumberOfTuples(len(points)) for i, p in enumerate(points): array_x.InsertValue(i, p[0]) array_y.InsertValue(i, p[1]) field = vtk.vtkFieldData() field.AddArray(array_x) field.AddArray(array_y) data = vtk.vtkDataObject() data.SetFieldData(field) plot = vtk.vtkXYPlotActor() plot.AddDataObjectInput(data) plot.SetDataObjectXComponent(0, 0) plot.SetDataObjectYComponent(0, 1) plot.SetXValuesToValue() plot.SetXTitle(title) plot.SetYTitle('') plot.ExchangeAxesOff() plot.PlotPointsOn() if not lines: plot.PlotLinesOff() plot.GetProperty().SetPointSize(5) plot.GetProperty().SetLineWidth(2) plot.SetNumberOfXLabels(3) #not working plot.GetProperty().SetColor(0, 0, 0) plot.GetProperty().SetOpacity(0.7) plot.SetPlotColor(0, c[0], c[1], c[2]) tprop = plot.GetAxisLabelTextProperty() tprop.SetColor(0, 0, 0) tprop.SetOpacity(0.7) tprop.SetFontFamily(0) tprop.BoldOff() tprop.ItalicOff() tprop.ShadowOff() tprop.SetFontSize(3) #not working plot.SetAxisTitleTextProperty(tprop) plot.SetAxisLabelTextProperty(tprop) plot.SetTitleTextProperty(tprop) if corner == 1: plot.GetPositionCoordinate().SetValue(.0, .8, 0) if corner == 2: plot.GetPositionCoordinate().SetValue(.7, .8, 0) if corner == 3: plot.GetPositionCoordinate().SetValue(.0, .0, 0) if corner == 4: plot.GetPositionCoordinate().SetValue(.7, .0, 0) plot.GetPosition2Coordinate().SetValue(.3, .2, 0) return plot
def status(self, s=None): ''' Set/Get the status of the button ''' if s is None: return self.states[self._status] if isinstance(s, str): s = self.states.index(s) self._status = s self.textproperty.SetLineOffset(self.offset) self.actor.SetInput(self.spacer + self.states[s] + self.spacer) s = s % len(self.colors) # to avoid mismatch self.textproperty.SetColor(colors.getColor(self.colors[s])) bcc = numpy.array(colors.getColor(self.bcolors[s])) self.textproperty.SetBackgroundColor(bcc) if self.showframe: self.textproperty.FrameOn() self.textproperty.SetFrameWidth(self.framewidth) self.textproperty.SetFrameColor(numpy.sqrt(bcc))
def light( self, pos=(1, 1, 1), fp=(0, 0, 0), deg=25, diffuse="y", ambient="r", specular="b", showsource=False, ): """ Generate a source of light placed at pos, directed to focal point fp. :param fp: focal Point, if this is a ``vtkActor`` use its position. :type fp: vtkActor, list :param deg: aperture angle of the light source :param showsource: if `True`, will show a vtk representation of the source of light as an extra actor .. hint:: |lights.py|_ """ if isinstance(fp, vtk.vtkActor): fp = fp.GetPosition() light = vtk.vtkLight() light.SetLightTypeToSceneLight() light.SetPosition(pos) light.SetPositional(1) light.SetConeAngle(deg) light.SetFocalPoint(fp) light.SetDiffuseColor(colors.getColor(diffuse)) light.SetAmbientColor(colors.getColor(ambient)) light.SetSpecularColor(colors.getColor(specular)) save_int = self.interactive self.show(interactive=0) self.interactive = save_int if showsource: lightActor = vtk.vtkLightActor() lightActor.SetLight(light) self.renderer.AddViewProp(lightActor) self.renderer.AddLight(light) return light
def showInset(self, *actors, **options): #pos=3, size=0.1, c='r', draggable=True): """Add a draggable inset space into a renderer. :param pos: icon position in the range [1-4] indicating one of the 4 corners, or it can be a tuple (x,y) as a fraction of the renderer size. :param float size: size of the square inset. :param bool draggable: if True the subrenderer space can be dragged around. .. hint:: |inset| |inset.py|_ """ pos = options.pop("pos", None) size = options.pop("size", 0.1) c = options.pop("c", 'r') draggable = options.pop("draggable", True) if not self.renderer: colors.printc( "~lightningWarning: Use showInset() after first rendering the scene.", c=3) save_int = self.interactive self.show(interactive=0) self.interactive = save_int widget = vtk.vtkOrientationMarkerWidget() r, g, b = colors.getColor(c) widget.SetOutlineColor(r, g, b) if len(actors) == 1: widget.SetOrientationMarker(actors[0]) else: widget.SetOrientationMarker(Assembly(utils.flatten(actors))) widget.SetInteractor(self.interactor) if utils.isSequence(pos): widget.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) else: if pos < 2: widget.SetViewport(0, 1 - 2 * size, size * 2, 1) elif pos == 2: widget.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) elif pos == 3: widget.SetViewport(0, 0, size * 2, size * 2) elif pos == 4: widget.SetViewport(1 - 2 * size, 0, 1, size * 2) widget.EnabledOn() widget.SetInteractive(draggable) self.widgets.append(widget) for a in actors: if a in self.actors: self.actors.remove(a) return widget
def cornerHistogram( values, bins=20, vrange=None, minbin=0, logscale=False, title="", c="g", bg="k", pos=1, s=0.2, lines=True, ): """ Build a histogram from a list of values in n bins. The resulting object is a 2D actor. Use *vrange* to restrict the range of the histogram. Use `pos` to assign its position: - 1, topleft, - 2, topright, - 3, bottomleft, - 4, bottomright, - (x, y), as fraction of the rendering window .. hint:: Example: |fitplanes.py|_ """ fs, edges = np.histogram(values, bins=bins, range=vrange) if minbin: fs = fs[minbin:-1] if logscale: fs = np.log10(fs + 1) pts = [] for i in range(len(fs)): pts.append([(edges[i] + edges[i + 1]) / 2, fs[i]]) plot = cornerPlot(pts, pos, s, title, c, bg, lines) plot.SetNumberOfYLabels(2) plot.SetNumberOfXLabels(3) tprop = vtk.vtkTextProperty() tprop.SetColor(colors.getColor(bg)) plot.SetAxisTitleTextProperty(tprop) plot.GetXAxisActor2D().SetLabelTextProperty(tprop) plot.GetXAxisActor2D().SetTitleTextProperty(tprop) plot.GetXAxisActor2D().SetFontFactor(0.5) plot.GetYAxisActor2D().SetLabelFactor(0.0) plot.GetYAxisActor2D().LabelVisibilityOff() return plot
def findCellsWithin(self, xbounds=(), ybounds=(), zbounds=(), c=None): """ Find cells that are within specified bounds. Setting a color will add a vtk array to colorize these cells. """ if len(xbounds) == 6: bnds = xbounds else: bnds = list(self.bounds()) if len(xbounds) == 2: bnds[0] = xbounds[0] bnds[1] = xbounds[1] if len(ybounds) == 2: bnds[2] = ybounds[0] bnds[3] = ybounds[1] if len(zbounds) == 2: bnds[4] = zbounds[0] bnds[5] = zbounds[1] cellIds = vtk.vtkIdList() self.cell_locator = vtk.vtkCellTreeLocator() self.cell_locator.SetDataSet(self.polydata()) self.cell_locator.BuildLocator() self.cell_locator.FindCellsWithinBounds(bnds, cellIds) if c is not None: cellData = vtk.vtkUnsignedCharArray() cellData.SetNumberOfComponents(3) cellData.SetName('CellsWithinBoundsColor') cellData.SetNumberOfTuples(self.polydata(False).GetNumberOfCells()) defcol = np.array(self.color()) * 255 for i in range(cellData.GetNumberOfTuples()): cellData.InsertTuple(i, defcol) self.polydata(False).GetCellData().SetScalars(cellData) self._mapper.ScalarVisibilityOn() flagcol = np.array(colors.getColor(c)) * 255 cids = [] for i in range(cellIds.GetNumberOfIds()): cid = cellIds.GetId(i) if c is not None: cellData.InsertTuple(cid, flagcol) cids.append(cid) return np.array(cids)
def changeLineColor(self, c, acts=None, t=None, duration=None): """Gradually change line color of the mesh edges for the input list of actors.""" if self.bookingMode: acts, t, duration, rng = self._parse(acts, t, duration) col2 = getColor(c) for tt in rng: inputvalues = [] for a in acts: col1 = a.lineColor() r = linInterpolate(tt, [t,t+duration], [col1[0], col2[0]]) g = linInterpolate(tt, [t,t+duration], [col1[1], col2[1]]) b = linInterpolate(tt, [t,t+duration], [col1[2], col2[2]]) inputvalues.append((r,g,b)) self.events.append((tt, self.changeLineColor, acts, inputvalues)) else: for i,a in enumerate(self._performers): a.lineColor(self._inputvalues[i]) return self
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 loadPCD(filename, c, alpha, legend): '''Return vtkActor from Point Cloud file format''' if not os.path.exists(filename): vc.printc('Error in loadPCD: Cannot find file', filename, c=1) return None f = open(filename, 'r') lines = f.readlines() f.close() start = False pts = [] N, expN = 0, 0 for text in lines: if start: if N >= expN: break l = text.split() pts.append([float(l[0]), float(l[1]), float(l[2])]) N += 1 if not start and 'POINTS' in text: expN = int(text.split()[1]) if not start and 'DATA ascii' in text: start = True if expN != N: vc.printc('Mismatch in pcd file', expN, len(pts), c='red') src = vtk.vtkPointSource() src.SetNumberOfPoints(len(pts)) src.Update() poly = src.GetOutput() for i, p in enumerate(pts): poly.GetPoints().SetPoint(i, p) if not poly: vc.printc('Unable to load', filename, c='red') return False actor = vu.makeActor(poly, vc.getColor(c), alpha) actor.GetProperty().SetPointSize(4) if legend: setattr(actor, 'legend', legend) if legend is True: setattr(actor, 'legend', os.path.basename(filename)) 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 ''' 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 _fcolor(self, c=None): if c is not None: self.GetProperty().SetColor(colors.getColor(c)) return self else: return np.array(self.GetProperty().GetColor())
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 Spheres(centers, r=1, c="r", alpha=1, 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. .. hint:: |manyspheres| |manyspheres.py|_ """ cisseq = False if utils.isSequence(c): cisseq = True if cisseq: if len(centers) > len(c): colors.printc("~times Mismatch in Spheres() colors", len(centers), len(c), c=1) exit() if len(centers) != len(c): colors.printc("~lightningWarning: 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("times Mismatch in Spheres() radius", len(centers), len(r), c=1) exit() if len(centers) != len(r): colors.printc("~lightning Warning: mismatch in Spheres() radius", len(centers), len(r)) if cisseq and risseq: colors.printc("~noentry 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() psrc = vtk.vtkPointSource() psrc.SetNumberOfPoints(len(centers)) psrc.Update() pd = psrc.GetOutput() vpts = pd.GetPoints() glyph = vtk.vtkGlyph3D() glyph.SetSourceConnection(src.GetOutputPort()) if cisseq: glyph.SetColorModeToColorByScalar() ucols = vtk.vtkUnsignedCharArray() ucols.SetNumberOfComponents(3) ucols.SetName("colors") for i, p in enumerate(centers): vpts.SetPoint(i, p) cx, cy, cz = colors.getColor(c[i]) ucols.InsertNextTuple3(cx * 255, cy * 255, cz * 255) 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()) actor = Actor() actor.SetMapper(mapper) actor.GetProperty().SetInterpolationToPhong() actor.GetProperty().SetOpacity(alpha) if cisseq: mapper.ScalarVisibilityOn() else: mapper.ScalarVisibilityOff() actor.GetProperty().SetColor(colors.getColor(c)) settings.collectable_actors.append(actor) return actor
def Text( txt, pos=3, normal=(0, 0, 1), s=1, depth=0.1, justify="bottom-left", c=None, alpha=1, bc=None, bg=None, font="courier", followcam=False, ): """ Returns a ``vtkActor`` that shows a 3D text. :param pos: position in 3D space, if an integer is passed [1,8], a 2D text is placed in one of the 4 corners: 1, bottom-left 2, bottom-right 3, top-left 4, top-right 5, bottom-middle 6, middle-right 7, middle-left 8, top-middle :type pos: list, int :param float s: size of text. :param float depth: text thickness. :param str justify: text justification (bottom-left, bottom-right, top-left, top-right, centered). :param bg: background color of corner annotations. Only applies of `pos` is ``int``. :param str font: either `arial`, `courier` or `times`. Only applies of `pos` is ``int``. :param followcam: if `True` the text will auto-orient itself to the active camera. A ``vtkCamera`` object can also be passed. :type followcam: bool, vtkCamera .. hint:: |colorcubes.py|_ |markpoint.py|_ |annotations.py|_ |colorcubes| |markpoint| """ if c is None: # automatic black or white if settings.plotter_instance and settings.plotter_instance.renderer: c = (0.9, 0.9, 0.9) if np.sum( settings.plotter_instance.renderer.GetBackground()) > 1.5: c = (0.1, 0.1, 0.1) else: c = (0.6, 0.6, 0.6) if isinstance(pos, int): if pos > 8: pos = 8 if pos < 1: pos = 1 ca = vtk.vtkCornerAnnotation() ca.SetNonlinearFontScaleFactor(s / 2.7) ca.SetText(pos - 1, str(txt)) ca.PickableOff() cap = ca.GetTextProperty() cap.SetColor(colors.getColor(c)) if font.lower() == "courier": cap.SetFontFamilyToCourier() elif font.lower() == "times": cap.SetFontFamilyToTimes() else: cap.SetFontFamilyToArial() if bg: bgcol = colors.getColor(bg) cap.SetBackgroundColor(bgcol) cap.SetBackgroundOpacity(alpha * 0.5) cap.SetFrameColor(bgcol) cap.FrameOn() setattr(ca, 'renderedAt', set()) settings.collectable_actors.append(ca) return ca 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() if isinstance(followcam, vtk.vtkCamera): ttactor.SetCamera(followcam) else: ttactor.SetCamera(settings.plotter_instance.camera) else: ttactor = Actor() ttactor.SetMapper(ttmapper) ttactor.GetProperty().SetColor(colors.getColor(c)) ttmapper.Update() bb = tt.GetOutput().GetBounds() dx, dy = (bb[1] - bb[0]) / 2 * s, (bb[3] - bb[2]) / 2 * s cm = np.array([(bb[1] + bb[0]) / 2, (bb[3] + bb[2]) / 2, (bb[5] + bb[4]) / 2]) * s shift = -cm if "cent" in justify: pass elif "bottom-left" in justify: shift += np.array([dx, dy, 0]) elif "top-left" in justify: shift += np.array([dx, -dy, 0]) elif "bottom-right" in justify: shift += np.array([-dx, dy, 0]) elif "top-right" in justify: shift += np.array([-dx, -dy, 0]) else: colors.printc("~lightning Text(): Unknown justify type", justify, c=1) 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(np.rad2deg(phi)) ttactor.RotateY(np.rad2deg(theta)) ttactor.SetPosition(pos + shift) if bc: # defines a specific color for the backface backProp = vtk.vtkProperty() backProp.SetDiffuseColor(colors.getColor(bc)) backProp.SetOpacity(alpha) ttactor.SetBackfaceProperty(backProp) ttactor.PickableOff() settings.collectable_actors.append(ttactor) return ttactor
def Glyph(actor, glyphObj, orientationArray="", scaleByVectorSize=False, c=None, alpha=1): """ At each vertex of a mesh, another mesh - a `'glyph'` - is shown with various orientation options and coloring. Color can be specfied as a colormap which maps the size of the orientation vectors in `orientationArray`. :param orientationArray: list of vectors, ``vtkAbstractArray`` or the name of an already existing points array. :type orientationArray: list, str, vtkAbstractArray :param bool scaleByVectorSize: glyph mesh is scaled by the size of the vectors. .. hint:: |glyphs.py|_ |glyphs_arrows.py|_ |glyphs| |glyphs_arrows| """ cmap = None # user passing a color map to map orientationArray sizes if c in list(colors._mapscales.cmap_d.keys()): cmap = c c = None # user is passing an array of point colors if utils.isSequence(c) and len(c) > 3: ucols = vtk.vtkUnsignedCharArray() ucols.SetNumberOfComponents(3) ucols.SetName("glyphRGB") for col in c: cl = colors.getColor(col) ucols.InsertNextTuple3(cl[0] * 255, cl[1] * 255, cl[2] * 255) actor.polydata().GetPointData().SetScalars(ucols) c = None if isinstance(glyphObj, Actor): glyphObj = glyphObj.clean().polydata() gly = vtk.vtkGlyph3D() gly.SetInputData(actor.polydata()) gly.SetSourceData(glyphObj) gly.SetColorModeToColorByScalar() if orientationArray != "": gly.OrientOn() gly.SetScaleFactor(1) if scaleByVectorSize: gly.SetScaleModeToScaleByVector() else: gly.SetScaleModeToDataScalingOff() if orientationArray == "normals" or orientationArray == "Normals": gly.SetVectorModeToUseNormal() elif isinstance(orientationArray, vtk.vtkAbstractArray): actor.GetMapper().GetInput().GetPointData().AddArray( orientationArray) actor.GetMapper().GetInput().GetPointData().SetActiveVectors( "glyph_vectors") gly.SetInputArrayToProcess(0, 0, 0, 0, "glyph_vectors") gly.SetVectorModeToUseVector() elif utils.isSequence(orientationArray): # passing a list actor.addPointVectors(orientationArray, "glyph_vectors") gly.SetInputArrayToProcess(0, 0, 0, 0, "glyph_vectors") else: # passing a name gly.SetInputArrayToProcess(0, 0, 0, 0, orientationArray) gly.SetVectorModeToUseVector() if cmap: gly.SetColorModeToColorByVector() else: gly.SetColorModeToColorByScalar() gly.Update() pd = gly.GetOutput() actor = Actor(pd, c, alpha) if cmap: lut = vtk.vtkLookupTable() lut.SetNumberOfTableValues(512) lut.Build() for i in range(512): r, g, b = colors.colorMap(i, cmap, 0, 512) lut.SetTableValue(i, r, g, b, 1) actor.mapper.SetLookupTable(lut) actor.mapper.ScalarVisibilityOn() actor.mapper.SetScalarModeToUsePointData() rng = pd.GetPointData().GetScalars().GetRange() actor.mapper.SetScalarRange(rng[0], rng[1]) actor.GetProperty().SetInterpolationToFlat() settings.collectable_actors.append(actor) return actor
def Tube(points, r=1, c="r", alpha=1, res=12): """Build a tube along the line defined by a set of points. :param r: constant radius or list of radii. :type r: float, list :param c: constant color or list of colors for each point. :type c: float, list .. hint:: |ribbon.py|_ |tube.py|_ |ribbon| |tube| """ ppoints = vtk.vtkPoints() # Generate the polyline ppoints.SetData(numpy_to_vtk(points, deep=True)) lines = vtk.vtkCellArray() lines.InsertNextCell(len(points)) for i in range(len(points)): lines.InsertCellPoint(i) polyln = vtk.vtkPolyData() polyln.SetPoints(ppoints) polyln.SetLines(lines) tuf = vtk.vtkTubeFilter() tuf.CappingOn() tuf.SetNumberOfSides(res) tuf.SetInputData(polyln) if utils.isSequence(r): arr = numpy_to_vtk(np.ascontiguousarray(r), deep=True) arr.SetName("TubeRadius") polyln.GetPointData().AddArray(arr) polyln.GetPointData().SetActiveScalars("TubeRadius") tuf.SetVaryRadiusToVaryRadiusByAbsoluteScalar() else: tuf.SetRadius(r) usingColScals = False if utils.isSequence(c) and len(c) != 3: usingColScals = True cc = vtk.vtkUnsignedCharArray() cc.SetName("TubeColors") cc.SetNumberOfComponents(3) cc.SetNumberOfTuples(len(c)) for i, ic in enumerate(c): r, g, b = colors.getColor(ic) cc.InsertTuple3(i, int(255 * r), int(255 * g), int(255 * b)) polyln.GetPointData().AddArray(cc) c = None tuf.Update() polytu = tuf.GetOutput() actor = Actor(polytu, c=c, alpha=alpha, computeNormals=0) actor.phong() if usingColScals: actor.mapper.SetScalarModeToUsePointFieldData() actor.mapper.ScalarVisibilityOn() actor.mapper.SelectColorArray("TubeColors") actor.mapper.Modified() actor.base = np.array(points[0]) actor.top = np.array(points[-1]) settings.collectable_actors.append(actor) return actor