def arrow(startPoint, endPoint, c='r', s=None, alpha=1, legend=None, texture=None, res=12, rwSize=(800, 800)): ''' Build a 3D arrow from `startPoint` to `endPoint` of section size `s`, expressed as the fraction of the window size. .. note:: If ``s=None`` the arrow is scaled proportionally to its length, otherwise it represents the fraction of the window size. ''' 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() tf.SetInputData(arr.GetOutput()) tf.SetTransform(t) tf.Update() actor = Actor(tf.GetOutput(), c, alpha, legend=legend, texture=texture) actor.GetProperty().SetInterpolationToPhong() actor.SetPosition(startPoint) actor.DragableOff() actor.PickableOff() actor.base = np.array(startPoint) actor.top = np.array(endPoint) return actor
def Arrow(startPoint, endPoint, s=None, c="r", alpha=1, res=12): """ Build a 3D arrow from `startPoint` to `endPoint` of section size `s`, expressed as the fraction of the window size. .. note:: If ``s=None`` the arrow is scaled proportionally to its length, otherwise it represents the fraction of the window size. |OrientedArrow| """ axis = np.array(endPoint) - np.array(startPoint) length = np.linalg.norm(axis) if length: 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(np.rad2deg(phi)) t.RotateY(np.rad2deg(theta)) t.RotateY(-90) # put it along Z if s: sz = 800.0 * s t.Scale(length, sz, sz) else: t.Scale(length, length, length) tf = vtk.vtkTransformPolyDataFilter() tf.SetInputData(arr.GetOutput()) tf.SetTransform(t) tf.Update() actor = Actor(tf.GetOutput(), c, alpha) actor.GetProperty().SetInterpolationToPhong() actor.SetPosition(startPoint) actor.DragableOff() actor.PickableOff() actor.base = np.array(startPoint) actor.top = np.array(endPoint) settings.collectable_actors.append(actor) return actor
def grid(pos=[0, 0, 0], normal=[0, 0, 1], sx=1, sy=1, c='g', bc='darkgreen', lw=1, alpha=1, legend=None, resx=10, resy=10): '''Return a grid plane. .. hint:: Example: `brownian2D.py <https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/brownian2D.py>`_ .. image:: https://user-images.githubusercontent.com/32848391/50738948-73ce8300-11d9-11e9-8ef6-fc4f64c4a9ce.gif ''' ps = vtk.vtkPlaneSource() ps.SetResolution(resx, resy) ps.Update() poly0 = ps.GetOutput() t0 = vtk.vtkTransform() t0.Scale(sx, sy, 1) tf0 = vtk.vtkTransformPolyDataFilter() tf0.SetInputData(poly0) tf0.SetTransform(t0) tf0.Update() poly = tf0.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() tf.SetInputData(poly) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() actor = Actor(pd, c=c, bc=bc, alpha=alpha, legend=legend) actor.GetProperty().SetRepresentationToWireframe() actor.GetProperty().SetLineWidth(lw) actor.SetPosition(pos) actor.PickableOff() return actor
def Grid(pos=(0, 0, 0), normal=(0, 0, 1), sx=1, sy=1, c="g", bc="darkgreen", lw=1, alpha=1, resx=10, resy=10): """Return a grid plane. .. hint:: |brownian2D| |brownian2D.py|_ """ ps = vtk.vtkPlaneSource() ps.SetResolution(resx, resy) ps.Update() poly0 = ps.GetOutput() t0 = vtk.vtkTransform() t0.Scale(sx, sy, 1) tf0 = vtk.vtkTransformPolyDataFilter() tf0.SetInputData(poly0) tf0.SetTransform(t0) tf0.Update() poly = tf0.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() tf.SetInputData(poly) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() actor = Actor(pd, c=c, bc=bc, alpha=alpha) actor.GetProperty().SetRepresentationToWireframe() actor.GetProperty().SetLineWidth(lw) actor.SetPosition(pos) actor.PickableOff() return actor
def plane(pos=[0, 0, 0], normal=[0, 0, 1], sx=1, sy=None, c='g', bc='darkgreen', alpha=1, legend=None, texture=None): ''' Draw a plane of size `sx` and `sy` oriented perpendicular to vector `normal` and so that it passes through point `pos`. ''' if sy is None: sy = sx ps = vtk.vtkPlaneSource() ps.SetResolution(1, 1) tri = vtk.vtkTriangleFilter() tri.SetInputConnection(ps.GetOutputPort()) tri.Update() poly = tri.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.Scale(sx, sy, 1) t.RotateY(theta * 57.3) t.RotateZ(phi * 57.3) tf = vtk.vtkTransformPolyDataFilter() tf.SetInputData(poly) tf.SetTransform(t) tf.Update() pd = tf.GetOutput() actor = Actor(pd, c=c, bc=bc, alpha=alpha, legend=legend, texture=texture) actor.SetPosition(pos) actor.PickableOff() 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 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 histogram2D(xvalues, yvalues, bins=12, norm=1, c='g', alpha=1, fill=False): ''' Build a 2D hexagonal histogram from a list of x and y values. bins, nr of bins for the smaller range in x or y norm, sets a scaling factor for the z axis fill, draw solid hexagons ''' 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+.5).astype(int) else: m = bins n = np.rint(dx/dy*m*1.2+.5).astype(int) src = vtk.vtkPointSource() src.SetNumberOfPoints(len(xvalues)) src.Update() pointsPolydata = src.GetOutput() values = list(zip(xvalues, yvalues)) zs = [[0.0]]*len(values) values = np.append(values, zs, axis=1) pointsPolydata.GetPoints().SetData(numpy_to_vtk(values, deep=True)) cloud = Actor(pointsPolydata) c1 = vc.getColor(c) c2 = np.array(c1)*.7 r = 0.47/n*1.2*dx hexs, binmax = [], 0 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/1.33, j/1.12, 0) c = c1 else: p = (i/1.33, j/1.12+0.443, 0) c = c2 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*5) 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() h = Actor(tf.GetOutput(), c=c, alpha=alpha) h.PickableOff() hexs.append(h) if ne > binmax: binmax = ne asse = Assembly(hexs) asse.PickableOff() asse.SetScale(1/n*1.2*dx, 1/m*dy, norm/binmax*(dx+dy)/4) asse.SetPosition(xmin,ymin,0) return asse
def addAxes(axtype=None, c=None): """Draw axes on scene. Available axes types: :param int axtype: - 0, no axes, - 1, draw three gray grid walls - 2, show cartesian axes from (0,0,0) - 3, show positive range of cartesian axes from (0,0,0) - 4, show a triad at bottom left - 5, show a cube at bottom left - 6, mark the corners of the bounding box - 7, draw a simple ruler at the bottom of the window - 8, show the ``vtkCubeAxesActor`` object - 9, show the bounding box outLine - 10, show three circles representing the maximum bounding box """ vp = settings.plotter_instance if axtype is not None: vp.axes = axtype # overrride r = vp.renderers.index(vp.renderer) if not vp.axes: return if c is None: # automatic black or white c = (0.9, 0.9, 0.9) if numpy.sum(vp.renderer.GetBackground()) > 1.5: c = (0.1, 0.1, 0.1) if not vp.renderer: return if vp.axes_exist[r]: return # calculate max actors bounds bns = [] for a in vp.actors: if a and a.GetPickable(): b = a.GetBounds() if b: bns.append(b) if len(bns): max_bns = numpy.max(bns, axis=0) min_bns = numpy.min(bns, axis=0) vbb = (min_bns[0], max_bns[1], min_bns[2], max_bns[3], min_bns[4], max_bns[5]) else: vbb = vp.renderer.ComputeVisiblePropBounds() max_bns = vbb min_bns = vbb sizes = (max_bns[1] - min_bns[0], max_bns[3] - min_bns[2], max_bns[5] - min_bns[4]) ############################################################ if vp.axes == 1 or vp.axes == True: # gray grid walls nd = 4 # number of divisions in the smallest axis off = -0.04 # label offset step = numpy.min(sizes) / nd if not step: # bad proportions, use vtkCubeAxesActor vp.addAxes(axtype=8, c=c) vp.axes = 1 return rx, ry, rz = numpy.rint(sizes / step).astype(int) if max([rx / ry, ry / rx, rx / rz, rz / rx, ry / rz, rz / ry]) > 15: # bad proportions, use vtkCubeAxesActor vp.addAxes(axtype=8, c=c) vp.axes = 1 return gxy = shapes.Grid(pos=(0.5, 0.5, 0), normal=[0, 0, 1], bc=None, resx=rx, resy=ry) gxz = shapes.Grid(pos=(0.5, 0, 0.5), normal=[0, 1, 0], bc=None, resx=rz, resy=rx) gyz = shapes.Grid(pos=(0, 0.5, 0.5), normal=[1, 0, 0], bc=None, resx=rz, resy=ry) gxy.alpha(0.06).wire(False).color(c).lineWidth(1) gxz.alpha(0.04).wire(False).color(c).lineWidth(1) gyz.alpha(0.04).wire(False).color(c).lineWidth(1) xa = shapes.Line([0, 0, 0], [1, 0, 0], c=c, lw=1) ya = shapes.Line([0, 0, 0], [0, 1, 0], c=c, lw=1) za = shapes.Line([0, 0, 0], [0, 0, 1], c=c, lw=1) xt, yt, zt, ox, oy, oz = [None] * 6 if vp.xtitle: xtitle = vp.xtitle if min_bns[0] <= 0 and max_bns[1] > 0: # mark x origin ox = shapes.Cube([-min_bns[0] / sizes[0], 0, 0], side=0.008, c=c) if len(vp.xtitle) == 1: # add axis length info xtitle = vp.xtitle + " /" + utils.precision(sizes[0], 4) wpos = [1 - (len(vp.xtitle) + 1) / 40, off, 0] xt = shapes.Text(xtitle, pos=wpos, normal=(0, 0, 1), s=0.025, c=c, justify="bottom-right") if vp.ytitle: if min_bns[2] <= 0 and max_bns[3] > 0: # mark y origin oy = shapes.Cube([0, -min_bns[2] / sizes[1], 0], side=0.008, c=c) yt = shapes.Text(vp.ytitle, pos=(0, 0, 0), normal=(0, 0, 1), s=0.025, c=c, justify="bottom-right") if len(vp.ytitle) == 1: wpos = [off, 1 - (len(vp.ytitle) + 1) / 40, 0] yt.pos(wpos) else: wpos = [off * 0.7, 1 - (len(vp.ytitle) + 1) / 40, 0] yt.rotateZ(90).pos(wpos) if vp.ztitle: if min_bns[4] <= 0 and max_bns[5] > 0: # mark z origin oz = shapes.Cube([0, 0, -min_bns[4] / sizes[2]], side=0.008, c=c) zt = shapes.Text(vp.ztitle, pos=(0, 0, 0), normal=(1, -1, 0), s=0.025, c=c, justify="bottom-right") if len(vp.ztitle) == 1: wpos = [off * 0.6, off * 0.6, 1 - (len(vp.ztitle) + 1) / 40] zt.rotate(90, (1, -1, 0)).pos(wpos) else: wpos = [off * 0.3, off * 0.3, 1 - (len(vp.ztitle) + 1) / 40] zt.rotate(180, (1, -1, 0)).pos(wpos) acts = [gxy, gxz, gyz, xa, ya, za, xt, yt, zt, ox, oy, oz] for a in acts: if a: a.PickableOff() aa = Assembly(acts) aa.pos(min_bns[0], min_bns[2], min_bns[4]) aa.SetScale(sizes) aa.PickableOff() vp.renderer.AddActor(aa) vp.axes_exist[r] = aa elif vp.axes == 2 or vp.axes == 3: vbb = vp.renderer.ComputeVisiblePropBounds() # to be double checked xcol, ycol, zcol = "db", "dg", "dr" s = 1 alpha = 1 centered = False x0, x1, y0, y1, z0, z1 = vbb dx, dy, dz = x1 - x0, y1 - y0, z1 - z0 aves = numpy.sqrt(dx * dx + dy * dy + dz * dz) / 2 x0, x1 = min(x0, 0), max(x1, 0) y0, y1 = min(y0, 0), max(y1, 0) z0, z1 = min(z0, 0), max(z1, 0) if vp.axes == 3: if x1 > 0: x0 = 0 if y1 > 0: y0 = 0 if z1 > 0: z0 = 0 dx, dy, dz = x1 - x0, y1 - y0, z1 - z0 acts = [] if x0 * x1 <= 0 or y0 * z1 <= 0 or z0 * z1 <= 0: # some ranges contain origin zero = shapes.Sphere(r=aves / 120 * s, c="k", alpha=alpha, res=10) acts += [zero] if len(vp.xtitle) and dx > aves / 100: xl = shapes.Cylinder([[x0, 0, 0], [x1, 0, 0]], r=aves / 250 * s, c=xcol, alpha=alpha) xc = shapes.Cone(pos=[x1, 0, 0], c=xcol, alpha=alpha, r=aves / 100 * s, height=aves / 25 * s, axis=[1, 0, 0], res=10) wpos = [ x1 - (len(vp.xtitle) + 1) * aves / 40 * s, -aves / 25 * s, 0 ] # aligned to arrow tip if centered: wpos = [(x0 + x1) / 2 - len(vp.xtitle) / 2 * aves / 40 * s, -aves / 25 * s, 0] xt = shapes.Text(vp.xtitle, pos=wpos, normal=(0, 0, 1), s=aves / 40 * s, c=xcol) acts += [xl, xc, xt] if len(vp.ytitle) and dy > aves / 100: yl = shapes.Cylinder([[0, y0, 0], [0, y1, 0]], r=aves / 250 * s, c=ycol, alpha=alpha) yc = shapes.Cone(pos=[0, y1, 0], c=ycol, alpha=alpha, r=aves / 100 * s, height=aves / 25 * s, axis=[0, 1, 0], res=10) wpos = [ -aves / 40 * s, y1 - (len(vp.ytitle) + 1) * aves / 40 * s, 0 ] if centered: wpos = [ -aves / 40 * s, (y0 + y1) / 2 - len(vp.ytitle) / 2 * aves / 40 * s, 0 ] yt = shapes.Text(vp.ytitle, pos=(0, 0, 0), normal=(0, 0, 1), s=aves / 40 * s, c=ycol) yt.rotate(90, [0, 0, 1]).pos(wpos) acts += [yl, yc, yt] if len(vp.ztitle) and dz > aves / 100: zl = shapes.Cylinder([[0, 0, z0], [0, 0, z1]], r=aves / 250 * s, c=zcol, alpha=alpha) zc = shapes.Cone(pos=[0, 0, z1], c=zcol, alpha=alpha, r=aves / 100 * s, height=aves / 25 * s, axis=[0, 0, 1], res=10) wpos = [ -aves / 50 * s, -aves / 50 * s, z1 - (len(vp.ztitle) + 1) * aves / 40 * s ] if centered: wpos = [ -aves / 50 * s, -aves / 50 * s, (z0 + z1) / 2 - len(vp.ztitle) / 2 * aves / 40 * s ] zt = shapes.Text(vp.ztitle, pos=(0, 0, 0), normal=(1, -1, 0), s=aves / 40 * s, c=zcol) zt.rotate(180, (1, -1, 0)).pos(wpos) acts += [zl, zc, zt] for a in acts: a.PickableOff() ass = Assembly(acts) ass.PickableOff() vp.renderer.AddActor(ass) vp.axes_exist[r] = ass elif vp.axes == 4: axact = vtk.vtkAxesActor() axact.SetShaftTypeToCylinder() axact.SetCylinderRadius(0.03) axact.SetXAxisLabelText(vp.xtitle) axact.SetYAxisLabelText(vp.ytitle) axact.SetZAxisLabelText(vp.ztitle) axact.GetXAxisShaftProperty().SetColor(0, 0, 1) axact.GetZAxisShaftProperty().SetColor(1, 0, 0) axact.GetXAxisTipProperty().SetColor(0, 0, 1) axact.GetZAxisTipProperty().SetColor(1, 0, 0) bc = numpy.array(vp.renderer.GetBackground()) if numpy.sum(bc) < 1.5: lc = (1, 1, 1) else: lc = (0, 0, 0) axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff() axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff() axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff() axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff() axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff() axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff() axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc) axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc) axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc) axact.PickableOff() icn = addIcon(axact, size=0.1) vp.axes_exist[r] = icn elif vp.axes == 5: axact = vtk.vtkAnnotatedCubeActor() axact.GetCubeProperty().SetColor(0.75, 0.75, 0.75) axact.SetTextEdgesVisibility(0) axact.SetFaceTextScale(0.4) axact.GetXPlusFaceProperty().SetColor(colors.getColor("b")) axact.GetXMinusFaceProperty().SetColor(colors.getColor("db")) axact.GetYPlusFaceProperty().SetColor(colors.getColor("g")) axact.GetYMinusFaceProperty().SetColor(colors.getColor("dg")) axact.GetZPlusFaceProperty().SetColor(colors.getColor("r")) axact.GetZMinusFaceProperty().SetColor(colors.getColor("dr")) axact.PickableOff() icn = addIcon(axact, size=0.06) vp.axes_exist[r] = icn elif vp.axes == 6: ocf = vtk.vtkOutlineCornerFilter() ocf.SetCornerFactor(0.1) largestact, sz = None, -1 for a in vp.actors: if a.GetPickable(): b = a.GetBounds() d = max(b[1] - b[0], b[3] - b[2], b[5] - b[4]) if sz < d: largestact = a sz = d if isinstance(largestact, Assembly): ocf.SetInputData(largestact.getActor(0).GetMapper().GetInput()) else: ocf.SetInputData(largestact.polydata()) ocf.Update() ocMapper = vtk.vtkHierarchicalPolyDataMapper() ocMapper.SetInputConnection(0, ocf.GetOutputPort(0)) ocActor = vtk.vtkActor() ocActor.SetMapper(ocMapper) bc = numpy.array(vp.renderer.GetBackground()) if numpy.sum(bc) < 1.5: lc = (1, 1, 1) else: lc = (0, 0, 0) ocActor.GetProperty().SetColor(lc) ocActor.PickableOff() vp.renderer.AddActor(ocActor) vp.axes_exist[r] = ocActor elif vp.axes == 7: # draws a simple ruler at the bottom of the window ls = vtk.vtkLegendScaleActor() ls.RightAxisVisibilityOff() ls.TopAxisVisibilityOff() ls.LegendVisibilityOff() ls.LeftAxisVisibilityOff() ls.GetBottomAxis().SetNumberOfMinorTicks(1) ls.GetBottomAxis().GetProperty().SetColor(c) ls.GetBottomAxis().GetLabelTextProperty().SetColor(c) ls.GetBottomAxis().GetLabelTextProperty().BoldOff() ls.GetBottomAxis().GetLabelTextProperty().ItalicOff() ls.GetBottomAxis().GetLabelTextProperty().ShadowOff() ls.PickableOff() vp.renderer.AddActor(ls) vp.axes_exist[r] = ls elif vp.axes == 8: ca = vtk.vtkCubeAxesActor() ca.SetBounds(vbb) if vp.camera: ca.SetCamera(vp.camera) else: ca.SetCamera(vp.renderer.GetActiveCamera()) ca.GetXAxesLinesProperty().SetColor(c) ca.GetYAxesLinesProperty().SetColor(c) ca.GetZAxesLinesProperty().SetColor(c) for i in range(3): ca.GetLabelTextProperty(i).SetColor(c) ca.GetTitleTextProperty(i).SetColor(c) ca.SetTitleOffset(5) ca.SetFlyMode(3) ca.SetXTitle(vp.xtitle) ca.SetYTitle(vp.ytitle) ca.SetZTitle(vp.ztitle) if vp.xtitle == "": ca.SetXAxisVisibility(0) ca.XAxisLabelVisibilityOff() if vp.ytitle == "": ca.SetYAxisVisibility(0) ca.YAxisLabelVisibilityOff() if vp.ztitle == "": ca.SetZAxisVisibility(0) ca.ZAxisLabelVisibilityOff() ca.PickableOff() vp.renderer.AddActor(ca) vp.axes_exist[r] = ca return elif vp.axes == 9: src = vtk.vtkCubeSource() src.SetXLength(vbb[1] - vbb[0]) src.SetYLength(vbb[3] - vbb[2]) src.SetZLength(vbb[5] - vbb[4]) src.Update() ca = Actor(src.GetOutput(), c=c, alpha=0.5, wire=1) ca.pos((vbb[0] + vbb[1]) / 2, (vbb[3] + vbb[2]) / 2, (vbb[5] + vbb[4]) / 2) ca.PickableOff() vp.renderer.AddActor(ca) vp.axes_exist[r] = ca elif vp.axes == 10: x0 = (vbb[0] + vbb[1]) / 2, (vbb[3] + vbb[2]) / 2, (vbb[5] + vbb[4]) / 2 rx, ry, rz = (vbb[1] - vbb[0]) / 2, (vbb[3] - vbb[2]) / 2, (vbb[5] - vbb[4]) / 2 rm = max(rx, ry, rz) xc = shapes.Disc(x0, (0, 0, 1), r1=rm, r2=rm, c='lr', bc=None, res=1, resphi=72) yc = shapes.Disc(x0, (0, 1, 0), r1=rm, r2=rm, c='lg', bc=None, res=1, resphi=72) zc = shapes.Disc(x0, (1, 0, 0), r1=rm, r2=rm, c='lb', bc=None, res=1, resphi=72) xc.clean().alpha(0.2).wire().lineWidth(2.5).PickableOff() yc.clean().alpha(0.2).wire().lineWidth(2.5).PickableOff() zc.clean().alpha(0.2).wire().lineWidth(2.5).PickableOff() ca = xc + yc + zc ca.PickableOff() vp.renderer.AddActor(ca) vp.axes_exist[r] = ca else: colors.printc('~bomb Keyword axes must be in range [0-10].', c=1) colors.printc(''' ~target Available axes types: 0 = no axes, 1 = draw three gray grid walls 2 = show cartesian axes from (0,0,0) 3 = show positive range of cartesian axes from (0,0,0) 4 = show a triad at bottom left 5 = show a cube at bottom left 6 = mark the corners of the bounding box 7 = draw a simple ruler at the bottom of the window 8 = show the vtkCubeAxesActor object 9 = show the bounding box outline 10 = show three circles representing the maximum bounding box ''', c=1, bold=0) if not vp.axes_exist[r]: vp.axes_exist[r] = True return
def text(txt, pos=(0, 0, 0), normal=(0, 0, 1), s=1, depth=0.1, justify='bottom-left', c='k', alpha=1, bc=None, texture=None, followcam=False, cam=None): ''' Returns a ``vtkActor`` that shows a 3D text. :param pos: position in 3D space, if an integer is passed [1,8], place text in one of the 4 corners. :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 bool followcam: if `True` the text will auto-orient itself to the cam. .. hint:: Example: `colorcubes.py <https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/colorcubes.py>`_ .. image:: https://user-images.githubusercontent.com/32848391/50738867-c0658e80-11d8-11e9-9e05-ac69b546b7ec.png ''' 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)) 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("text(): Unknown justify type", justify, c=1) # 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 + shift) 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) ttactor.PickableOff() return ttactor