def _cellColors(scale, scalars, cmap, alpha): mapper = scale.GetMapper() cpoly = mapper.GetInput() n = len(scalars) lut = vtk.vtkLookupTable() lut.SetNumberOfTableValues(n) lut.Build() for i in range(n): r, g, b = colors.colorMap(i, cmap, 0, n) lut.SetTableValue(i, r, g, b, alpha) arr = numpy_to_vtk(numpy.ascontiguousarray(scalars), deep=True) vmin, vmax = numpy.min(scalars), numpy.max(scalars) mapper.SetScalarRange(vmin, vmax) mapper.SetLookupTable(lut) mapper.ScalarVisibilityOn() cpoly.GetCellData().SetScalars(arr)
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 cellColors(actor, scalars, cmap='jet'): """ Set individual cell colors by setting a scalar """ poly = polydata(actor, False) if len(scalars) != poly.GetNumberOfCells(): colors.printc('Number of scalars != nr. of cells', 1) exit() lut = vtk.vtkLookupTable() lut.SetNumberOfTableValues(len(scalars)) lut.Build() vmin, vmax = np.min(scalars), np.max(scalars) n = len(scalars) for i in range(n): c = colors.colorMap(i, cmap, 0, n) lut.SetTableValue(i, c[0], c[1], c[2], 1) arr = numpy_to_vtk(np.ascontiguousarray(scalars), deep=True) arr.SetName('cellcolors_' + cmap) poly.GetCellData().AddArray(arr) poly.GetCellData().SetActiveScalars('cellcolors_' + cmap) actor.GetMapper().SetScalarRange(vmin, vmax) actor.GetMapper().SetLookupTable(lut) actor.GetMapper().ScalarVisibilityOn()
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 polarHistogram( values, title="", bins=10, r1=0.25, r2=1, phigap=3, rgap=0.05, lpos=1, lsize=0.05, c=None, bc="k", alpha=1, cmap=None, deg=False, vmin=None, vmax=None, labels=(), showDisc=True, showLines=True, showAngles=True, showErrors=False, ): """ Polar histogram with errorbars. :param str title: histogram title :param int bins: number of bins in phi :param float r1: inner radius :param float r2: outer radius :param float phigap: gap angle btw 2 radial bars, in degrees :param float rgap: gap factor along radius of numeric angle labels :param float lpos: label gap factor along radius :param float lsize: label size :param c: color of the histogram bars, can be a list of length `bins`. :param bc: color of the frame and labels :param alpha: alpha of the frame :param str cmap: color map name :param bool deg: input array is in degrees :param float vmin: minimum value of the radial axis :param float vmax: maximum value of the radial axis :param list labels: list of labels, must be of length `bins` :param bool showDisc: show the outer ring axis :param bool showLines: show lines to the origin :param bool showAngles: show angular values :param bool showErrors: show error bars |polarHisto| |polarHisto.py|_ """ k = 180 / np.pi if deg: values = np.array(values) / k dp = np.pi / bins vals = [] for v in values: # normalize range t = np.arctan2(np.sin(v), np.cos(v)) if t < 0: t += 2 * np.pi vals.append(t - dp) histodata, edges = np.histogram(vals, bins=bins, range=(-dp, 2 * np.pi - dp)) thetas = [] for i in range(bins): thetas.append((edges[i] + edges[i + 1]) / 2) if vmin is None: vmin = np.min(histodata) if vmax is None: vmax = np.max(histodata) errors = np.sqrt(histodata) r2e = r1 + r2 if showErrors: r2e += np.max(errors) / vmax * 1.5 back = None if showDisc: back = shapes.Disc(r1=r2e, r2=r2e * 1.01, c=bc, res=1, resphi=360) back.z(-0.01).lighting(diffuse=0, ambient=1).alpha(alpha) slices = [] lines = [] angles = [] labs = [] errbars = [] for i, t in enumerate(thetas): r = histodata[i] / vmax * r2 d = shapes.Disc((0, 0, 0), r1, r1 + r, res=1, resphi=360) delta = dp - np.pi / 2 - phigap / k d.cutWithPlane(normal=(np.cos(t + delta), np.sin(t + delta), 0)) d.cutWithPlane(normal=(np.cos(t - delta), np.sin(t - delta), 0)) if cmap is not None: cslice = colors.colorMap(histodata[i], cmap, vmin, vmax) d.color(cslice) else: if c is None: d.color(i) elif utils.isSequence(c) and len(c) == bins: d.color(c[i]) else: d.color(c) slices.append(d) ct, st = np.cos(t), np.sin(t) if showErrors: showLines = False err = np.sqrt(histodata[i]) / vmax * r2 errl = shapes.Line( ((r1 + r - err) * ct, (r1 + r - err) * st, 0.01), ((r1 + r + err) * ct, (r1 + r + err) * st, 0.01), ) errl.alpha(alpha).lw(3).color(bc) errbars.append(errl) if showDisc: if showLines: l = shapes.Line((0, 0, -0.01), (r2e * ct * 1.03, r2e * st * 1.03, -0.01)) lines.append(l) elif showAngles: # just the ticks l = shapes.Line( (r2e * ct * 0.98, r2e * st * 0.98, -0.01), (r2e * ct * 1.03, r2e * st * 1.03, -0.01), ) lines.append(l) if showAngles: if 0 <= t < np.pi / 2: ju = "bottom-left" elif t == np.pi / 2: ju = "bottom-center" elif np.pi / 2 < t <= np.pi: ju = "bottom-right" elif np.pi < t < np.pi * 3 / 2: ju = "top-right" elif t == np.pi * 3 / 2: ju = "top-center" else: ju = "top-left" a = shapes.Text(int(t * k), pos=(0, 0, 0), s=lsize, depth=0, justify=ju) a.pos(r2e * ct * (1 + rgap), r2e * st * (1 + rgap), -0.01) angles.append(a) if len(labels) == bins: lab = shapes.Text(labels[i], (0, 0, 0), s=lsize, depth=0, justify="center") lab.pos(r2e * ct * (1 + rgap) * lpos / 2, r2e * st * (1 + rgap) * lpos / 2, 0.01) labs.append(lab) ti = None if title: ti = shapes.Text(title, (0, 0, 0), s=lsize * 2, depth=0, justify="top-center") ti.pos(0, -r2e * 1.15, 0.01) mrg = merge(back, lines, angles, labs, ti) if mrg: mrg.color(bc).alpha(alpha).lighting(diffuse=0, ambient=1) rh = Assembly(slices + errbars + [mrg]) rh.base = np.array([0, 0, 0]) rh.top = np.array([0, 0, 1]) return rh
def hexHistogram( xvalues, yvalues, xtitle="", ytitle="", ztitle="", bins=12, norm=1, fill=True, c=None, cmap="terrain_r", alpha=1, ): """ Build a hexagonal histogram from a list of x and y values. :param bool bins: nr of bins for the smaller range in x or y. :param float norm: sets a scaling factor for the z axis (freq. axis). :param bool fill: draw solid hexagons. :param str cmap: color map name for elevation. |histoHexagonal| |histoHexagonal.py|_ """ if xtitle: from vtkplotter import settings settings.xtitle = xtitle if ytitle: from vtkplotter import settings settings.ytitle = ytitle if ztitle: from vtkplotter import settings settings.ztitle = ztitle xmin, xmax = np.min(xvalues), np.max(xvalues) ymin, ymax = np.min(yvalues), np.max(yvalues) dx, dy = xmax - xmin, ymax - ymin if xmax - xmin < ymax - ymin: n = bins m = np.rint(dy / dx * n / 1.2 + 0.5).astype(int) else: m = bins n = np.rint(dx / dy * m * 1.2 + 0.5).astype(int) src = vtk.vtkPointSource() src.SetNumberOfPoints(len(xvalues)) src.Update() pointsPolydata = src.GetOutput() #values = list(zip(xvalues, yvalues)) values = np.stack((xvalues, yvalues), axis=1) zs = [[0.0]] * len(values) values = np.append(values, zs, axis=1) pointsPolydata.GetPoints().SetData(numpy_to_vtk(values, deep=True)) cloud = Actor(pointsPolydata) col = None if c is not None: col = colors.getColor(c) hexs, binmax = [], 0 ki, kj = 1.33, 1.12 r = 0.47 / n * 1.2 * dx for i in range(n + 3): for j in range(m + 2): cyl = vtk.vtkCylinderSource() cyl.SetResolution(6) cyl.CappingOn() cyl.SetRadius(0.5) cyl.SetHeight(0.1) cyl.Update() t = vtk.vtkTransform() if not i % 2: p = (i / ki, j / kj, 0) else: p = (i / ki, j / kj + 0.45, 0) q = (p[0] / n * 1.2 * dx + xmin, p[1] / m * dy + ymin, 0) ids = cloud.closestPoint(q, radius=r, returnIds=True) ne = len(ids) if fill: t.Translate(p[0], p[1], ne / 2) t.Scale(1, 1, ne * 10) else: t.Translate(p[0], p[1], ne) t.RotateX(90) # put it along Z tf = vtk.vtkTransformPolyDataFilter() tf.SetInputData(cyl.GetOutput()) tf.SetTransform(t) tf.Update() if c is None: col = i h = Actor(tf.GetOutput(), c=col, alpha=alpha).flat() h.GetProperty().SetSpecular(0) h.GetProperty().SetDiffuse(1) h.PickableOff() hexs.append(h) if ne > binmax: binmax = ne if cmap is not None: for h in hexs: z = h.GetBounds()[5] col = colors.colorMap(z, cmap, 0, binmax) h.color(col) asse = Assembly(hexs) asse.SetScale(1.2 / n * dx, 1 / m * dy, norm / binmax * (dx + dy) / 4) asse.SetPosition(xmin, ymin, 0) return asse
def RayCaster(volume): """ Generate a ``Plotter`` window for Volume rendering using ray casting. Returns the ``Plotter`` object. """ vp = settings.plotter_instance if not vp: vp = Plotter(axes=4, bg='bb') volumeProperty = volume.GetProperty() img = volume.imagedata() if volume.dimensions()[2] < 3: print("Error in raycaster: not enough depth", volume.dimensions()) return vp printc("GPU Ray-casting tool", c="b", invert=1) smin, smax = img.GetScalarRange() x0alpha = smin + (smax - smin) * 0.25 x1alpha = smin + (smax - smin) * 0.5 x2alpha = smin + (smax - smin) * 1.0 ############################## color map slider # Create transfer mapping scalar value to color cmaps = [ "jet", "viridis", "bone", "hot", "plasma", "winter", "cool", "gist_earth", "coolwarm", "tab10", ] cols_cmaps = [] for cm in cmaps: cols = colorMap(range(0, 21), cm, 0, 20) # sample 20 colors cols_cmaps.append(cols) Ncols = len(cmaps) csl = (0.9, 0.9, 0.9) if sum(getColor(vp.renderer.GetBackground())) > 1.5: csl = (0.1, 0.1, 0.1) def sliderColorMap(widget, event): sliderRep = widget.GetRepresentation() k = int(sliderRep.GetValue()) sliderRep.SetTitleText(cmaps[k]) volume.color(cmaps[k]) w1 = vp.addSlider2D( sliderColorMap, 0, Ncols - 1, value=0, showValue=0, title=cmaps[0], c=csl, pos=[(0.8, 0.05), (0.965, 0.05)], ) w1.GetRepresentation().SetTitleHeight(0.018) ############################## alpha sliders # Create transfer mapping scalar value to opacity opacityTransferFunction = volumeProperty.GetScalarOpacity() def setOTF(): opacityTransferFunction.RemoveAllPoints() opacityTransferFunction.AddPoint(smin, 0.0) opacityTransferFunction.AddPoint(smin + (smax - smin) * 0.1, 0.0) opacityTransferFunction.AddPoint(x0alpha, _alphaslider0) opacityTransferFunction.AddPoint(x1alpha, _alphaslider1) opacityTransferFunction.AddPoint(x2alpha, _alphaslider2) setOTF() def sliderA0(widget, event): global _alphaslider0 _alphaslider0 = widget.GetRepresentation().GetValue() setOTF() vp.addSlider2D(sliderA0, 0, 1, value=_alphaslider0, pos=[(0.84, 0.1), (0.84, 0.26)], c=csl, showValue=0) def sliderA1(widget, event): global _alphaslider1 _alphaslider1 = widget.GetRepresentation().GetValue() setOTF() vp.addSlider2D(sliderA1, 0, 1, value=_alphaslider1, pos=[(0.89, 0.1), (0.89, 0.26)], c=csl, showValue=0) def sliderA2(widget, event): global _alphaslider2 _alphaslider2 = widget.GetRepresentation().GetValue() setOTF() w2 = vp.addSlider2D(sliderA2, 0, 1, value=_alphaslider2, pos=[(0.96, 0.1), (0.96, 0.26)], c=csl, showValue=0, title="Opacity levels") w2.GetRepresentation().SetTitleHeight(0.016) # add a button def buttonfuncMode(): s = volume.mode() snew = (s + 1) % 2 volume.mode(snew) bum.switch() bum = vp.addButton( buttonfuncMode, pos=(0.7, 0.035), states=["composite", "max proj."], c=["bb", "gray"], bc=["gray", "bb"], # colors of states font="arial", size=16, bold=0, italic=False, ) bum.status(volume.mode()) def CheckAbort(obj, event): if obj.GetEventPending() != 0: obj.SetAbortRender(1) vp.window.AddObserver("AbortCheckEvent", CheckAbort) # add histogram of scalar from vtkplotter.pyplot import cornerHistogram plot = cornerHistogram(volume.getPointArray(), bins=25, logscale=1, c="gray", bg="gray", pos=(0.78, 0.065)) plot.GetPosition2Coordinate().SetValue(0.197, 0.20, 0) plot.GetXAxisActor2D().SetFontFactor(0.7) plot.GetProperty().SetOpacity(0.5) vp.add(plot) vp.add(volume) return vp