vedo.addons
Create additional objects like axes, legends, lights, etc.
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3import numpy as np 4 5try: 6 import vedo.vtkclasses as vtk 7except ImportError: 8 import vtkmodules.all as vtk 9 10import vedo 11from vedo import settings 12from vedo import utils 13from vedo import shapes 14from vedo.assembly import Assembly, Group 15from vedo.colors import get_color, build_lut, color_map, printc 16from vedo.mesh import Mesh 17from vedo.pointcloud import Points, Point, merge 18from vedo.tetmesh import TetMesh 19from vedo.volume import Volume 20 21__docformat__ = "google" 22 23__doc__ = """ 24Create additional objects like axes, legends, lights, etc. 25 26![](https://vedo.embl.es/images/pyplot/customAxes2.png) 27""" 28 29__all__ = [ 30 "ScalarBar", 31 "ScalarBar3D", 32 "Slider2D", 33 "Slider3D", 34 "Icon", 35 "LegendBox", 36 "Light", 37 "Axes", 38 "RendererFrame", 39 "Ruler", 40 "RulerAxes", 41 "Ruler2D", 42 "DistanceTool", 43 "SplineTool", 44 "Goniometer", 45 "Button", 46 "Flagpost", 47 "ProgressBarWidget", 48 "BoxCutter", 49 "PlaneCutter", 50 "SphereCutter", 51] 52 53######################################################################################## 54class Flagpost(vtk.vtkFlagpoleLabel): 55 """ 56 Create a flag post style element to describe an object. 57 """ 58 59 def __init__( 60 self, 61 txt="", 62 base=(0, 0, 0), 63 top=(0, 0, 1), 64 s=1, 65 c="k9", 66 bc="k1", 67 alpha=1, 68 lw=0, 69 font="Calco", 70 justify="center-left", 71 vspacing=1, 72 ): 73 """ 74 Create a flag post style element to describe an object. 75 76 Arguments: 77 txt : (str) 78 Text to display. The default is the filename or the object name. 79 base : (list) 80 position of the flag anchor point. 81 top : (list) 82 a 3D displacement or offset. 83 s : (float) 84 size of the text to be shown 85 c : (list) 86 color of text and line 87 bc : (list) 88 color of the flag background 89 alpha : (float) 90 opacity of text and box. 91 lw : (int) 92 line with of box frame. The default is 0. 93 font : (str) 94 font name. Use a monospace font for better rendering. The default is "Calco". 95 Type `vedo -r fonts` for a font demo. 96 Check [available fonts here](https://vedo.embl.es/fonts). 97 justify : (str) 98 internal text justification. The default is "center-left". 99 vspacing : (float) 100 vertical spacing between lines. 101 102 Examples: 103 - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/examples/other/flag_labels2.py) 104 105 ![](https://vedo.embl.es/images/other/flag_labels2.png) 106 """ 107 108 vtk.vtkFlagpoleLabel.__init__(self) 109 110 base = utils.make3d(base) 111 top = utils.make3d(top) 112 113 self.SetBasePosition(*base) 114 self.SetTopPosition(*top) 115 116 self.SetFlagSize(s) 117 self.SetInput(txt) 118 self.PickableOff() 119 120 self.GetProperty().LightingOff() 121 self.GetProperty().SetLineWidth(lw + 1) 122 123 prop = self.GetTextProperty() 124 if bc is not None: 125 prop.SetBackgroundColor(get_color(bc)) 126 127 prop.SetOpacity(alpha) 128 prop.SetBackgroundOpacity(alpha) 129 if bc is not None and len(bc) == 4: 130 prop.SetBackgroundRGBA(alpha) 131 132 c = get_color(c) 133 prop.SetColor(c) 134 self.GetProperty().SetColor(c) 135 136 prop.SetFrame(bool(lw)) 137 prop.SetFrameWidth(lw) 138 prop.SetFrameColor(prop.GetColor()) 139 140 prop.SetFontFamily(vtk.VTK_FONT_FILE) 141 fl = utils.get_font_path(font) 142 prop.SetFontFile(fl) 143 prop.ShadowOff() 144 prop.BoldOff() 145 prop.SetOpacity(alpha) 146 prop.SetJustificationToLeft() 147 if "top" in justify: 148 prop.SetVerticalJustificationToTop() 149 if "bottom" in justify: 150 prop.SetVerticalJustificationToBottom() 151 if "cent" in justify: 152 prop.SetVerticalJustificationToCentered() 153 prop.SetJustificationToCentered() 154 if "left" in justify: 155 prop.SetJustificationToLeft() 156 if "right" in justify: 157 prop.SetJustificationToRight() 158 prop.SetLineSpacing(vspacing * 1.2) 159 self.SetUseBounds(False) 160 161 def text(self, value): 162 self.SetInput(value) 163 return self 164 165 def on(self): 166 self.VisibilityOn() 167 return self 168 169 def off(self): 170 self.VisibilityOff() 171 return self 172 173 def toggle(self): 174 self.SetVisibility(not self.GetVisibility()) 175 return self 176 177 def use_bounds(self, value=True): 178 self.SetUseBounds(value) 179 return self 180 181 def color(self, c): 182 c = get_color(c) 183 self.GetTextProperty().SetColor(c) 184 self.GetProperty().SetColor(c) 185 return self 186 187 def pos(self, p): 188 p = np.asarray(p) 189 self.top = self.top - self.base + p 190 self.base = p 191 return self 192 193 @property 194 def base(self): 195 return np.array(self.GetBasePosition()) 196 197 @property 198 def top(self): 199 return np.array(self.GetTopPosition()) 200 201 @base.setter 202 def base(self, value): 203 self.SetBasePosition(*value) 204 205 @top.setter 206 def top(self, value): 207 self.SetTopPosition(*value) 208 209 210 211########################################################################################### 212class LegendBox(shapes.TextBase, vtk.vtkLegendBoxActor): 213 """ 214 Create a 2D legend box. 215 """ 216 def __init__( 217 self, 218 entries=(), 219 nmax=12, 220 c=None, 221 font="", 222 width=0.18, 223 height=None, 224 padding=2, 225 bg="k8", 226 alpha=0.25, 227 pos="top-right", 228 markers=None, 229 ): 230 """ 231 Create a 2D legend box for the list of specified objects. 232 233 Arguments: 234 nmax : (int) 235 max number of legend entries 236 c : (color) 237 text color, leave as None to pick the mesh color automatically 238 font : (str) 239 Check [available fonts here](https://vedo.embl.es/fonts) 240 width : (float) 241 width of the box as fraction of the window width 242 height : (float) 243 height of the box as fraction of the window height 244 padding : (int) 245 padding space in units of pixels 246 bg : (color) 247 background color of the box 248 alpha: (float) 249 opacity of the box 250 pos : (str, list) 251 position of the box, can be either a string or a (x,y) screen position in range [0,1] 252 253 Examples: 254 - [legendbox.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/legendbox.py) 255 - [flag_labels1.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels1.py) 256 - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels2.py) 257 258 ![](https://vedo.embl.es/images/other/flag_labels.png) 259 """ 260 vtk.vtkLegendBoxActor.__init__(self) 261 shapes.TextBase.__init__(self) 262 263 self.name = "LegendBox" 264 self.entries = entries[:nmax] 265 self.property = self.GetEntryTextProperty() 266 267 n = 0 268 texts = [] 269 for e in self.entries: 270 ename = e.name 271 if "legend" in e.info.keys(): 272 if not e.info["legend"]: 273 ename = "" 274 else: 275 ename = str(e.info["legend"]) 276 277 if not isinstance(e, vtk.vtkActor): 278 ename = "" 279 if ename: 280 n += 1 281 texts.append(ename) 282 self.SetNumberOfEntries(n) 283 284 if not n: 285 return 286 287 self.ScalarVisibilityOff() 288 self.PickableOff() 289 self.SetPadding(padding) 290 291 self.property.ShadowOff() 292 self.property.BoldOff() 293 294 # self.property.SetJustificationToLeft() # no effect 295 # self.property.SetVerticalJustificationToTop() 296 297 if not font: 298 font = settings.default_font 299 300 self.font(font) 301 302 n = 0 303 for i in range(len(self.entries)): 304 ti = texts[i] 305 if not ti: 306 continue 307 e = entries[i] 308 if c is None: 309 col = e.GetProperty().GetColor() 310 if col == (1, 1, 1): 311 col = (0.2, 0.2, 0.2) 312 else: 313 col = get_color(c) 314 if markers is None: # default 315 poly = e.inputdata() 316 else: 317 marker = markers[i] if utils.is_sequence(markers) else markers 318 if isinstance(marker, vedo.Points): 319 poly = marker.clone(deep=False).normalize().shift(0, 1, 0).polydata() 320 else: # assume string marker 321 poly = vedo.shapes.Marker(marker, s=1).shift(0, 1, 0).polydata() 322 323 self.SetEntry(n, poly, ti, col) 324 n += 1 325 326 self.SetWidth(width) 327 if height is None: 328 self.SetHeight(width / 3.0 * n) 329 else: 330 self.SetHeight(height) 331 332 sx, sy = 1 - self.GetWidth(), 1 - self.GetHeight() 333 if pos == 1 or ("top" in pos and "left" in pos): 334 self.GetPositionCoordinate().SetValue(0, sy) 335 elif pos == 2 or ("top" in pos and "right" in pos): 336 self.GetPositionCoordinate().SetValue(sx, sy) 337 elif pos == 3 or ("bottom" in pos and "left" in pos): 338 self.GetPositionCoordinate().SetValue(0, 0) 339 elif pos == 4 or ("bottom" in pos and "right" in pos): 340 self.GetPositionCoordinate().SetValue(sx, 0) 341 if alpha: 342 self.UseBackgroundOn() 343 self.SetBackgroundColor(get_color(bg)) 344 self.SetBackgroundOpacity(alpha) 345 else: 346 self.UseBackgroundOff() 347 self.LockBorderOn() 348 349 350class Button(vtk.vtkTextActor): 351 """ 352 Build a Button object. 353 """ 354 def __init__( 355 self, 356 fnc=None, 357 states=("Button"), 358 c=("white"), 359 bc=("green4"), 360 pos=(0.7, 0.05), 361 size=24, 362 font=None, 363 bold=False, 364 italic=False, 365 alpha=1, 366 angle=0, 367 name="Button", 368 ): 369 """ 370 Build a Button object to be shown in the rendering window. 371 372 Arguments: 373 fnc : (function) 374 external function to be called by the widget 375 states : (list) 376 the list of possible states, eg. ['On', 'Off'] 377 c : (list) 378 the list of colors for each state eg. ['red3', 'green5'] 379 bc : (list) 380 the list of background colors for each state 381 pos : (list, str) 382 2D position in pixels from left-bottom corner 383 size : (int) 384 size of button font 385 font : (str) 386 font type 387 bold : (bool) 388 set bold font face 389 italic : (bool) 390 italic font face 391 alpha : (float) 392 opacity level 393 angle : (float) 394 anticlockwise rotation in degrees 395 name : (str) 396 name of the button (useful for multiple buttons in callbacks) 397 398 Examples: 399 - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py) 400 401 Examples: 402 - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py) 403 404 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 405 406 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 407 408 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 409 """ 410 vtk.vtkTextActor.__init__(self) 411 412 self.status_idx = 0 413 self.states = states 414 415 if not utils.is_sequence(c): 416 c = [c] 417 self.colors = c 418 419 if not utils.is_sequence(bc): 420 bc = [bc] 421 self.bcolors = bc 422 423 assert len(c) == len(bc), "in Button color number mismatch!" 424 425 self.function = fnc 426 self.function_id = None 427 self.name = name 428 429 self.GetActualPositionCoordinate().SetCoordinateSystemToNormalizedViewport() 430 self.SetPosition(pos[0], pos[1]) 431 432 self.offset = 5 433 self.spacer = " " 434 435 self.len_states = max([len(s) for s in states]) 436 437 self.text_property = self.GetTextProperty() 438 self.text_property.SetJustificationToCentered() 439 440 if not font: 441 font = settings.default_font 442 443 if font.lower() == "courier": 444 self.text_property.SetFontFamilyToCourier() 445 elif font.lower() == "times": 446 self.text_property.SetFontFamilyToTimes() 447 elif font.lower() == "arial": 448 self.text_property.SetFontFamilyToArial() 449 else: 450 self.text_property.SetFontFamily(vtk.VTK_FONT_FILE) 451 self.text_property.SetFontFile(utils.get_font_path(font)) 452 self.text_property.SetFontSize(size) 453 454 self.text_property.SetBackgroundOpacity(alpha) 455 456 self.text_property.BoldOff() 457 if bold: 458 self.text_property.BoldOn() 459 460 self.text_property.ItalicOff() 461 if italic: 462 self.text_property.ItalicOn() 463 464 self.text_property.ShadowOff() 465 self.text_property.SetOrientation(angle) 466 self.text_property.SetLineOffset(self.offset) 467 468 self.hasframe = hasattr(self.text_property, "FrameOn") 469 470 self.status(0) 471 472 def text(self, txt="", c=None): 473 if txt: 474 self.SetInput(self.spacer + str(txt) + self.spacer) 475 else: 476 return self.GetInput() 477 478 if c is not None: 479 self.text_property.SetColor(get_color(c)) 480 return self 481 482 def backcolor(self, c): 483 self.text_property.SetBackgroundColor(get_color(c)) 484 return self 485 486 def frame(self, lw=None, c=None): 487 if self.hasframe: 488 self.text_property.FrameOn() 489 if lw is not None: 490 if lw > 0: 491 self.text_property.FrameOn() 492 self.text_property.SetFrameWidth(lw) 493 else: 494 self.text_property.FrameOff() 495 return self 496 if c is not None: 497 self.text_property.SetFrameColor(get_color(c)) 498 return self 499 500 def status(self, s=None): 501 """ 502 Set/Get the status of the button. 503 """ 504 if s is None: 505 return self.states[self.status_idx] 506 507 if isinstance(s, str): 508 s = self.states.index(s) 509 self.status_idx = s 510 self.text(self.states[s]) 511 s = s % len(self.bcolors) 512 self.text(c=self.colors[s]) 513 self.backcolor(self.bcolors[s]) 514 return self 515 516 def switch(self): 517 """ 518 Change/cycle button status to the next defined status in states list. 519 """ 520 self.status_idx = (self.status_idx + 1) % len(self.states) 521 self.status(self.status_idx) 522 return self 523 524 525##################################################################### 526class SplineTool(vtk.vtkContourWidget): 527 """ 528 Spline tool, draw a spline through a set of points interactively. 529 """ 530 531 def __init__(self, points, pc="k", ps=8, lc="r4", ac="g5", lw=2, closed=False, ontop=True): 532 """ 533 Spline tool, draw a spline through a set of points interactively. 534 535 Arguments: 536 points : (list), Points 537 initial set of points. 538 pc : (str) 539 point color. 540 ps : (int) 541 point size. 542 lc : (str) 543 line color. 544 ac : (str) 545 active point color. 546 lw : (int) 547 line width. 548 closed : (bool) 549 spline is closed or open. 550 ontop : (bool) 551 show it always on top of other objects. 552 553 Examples: 554 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 555 556 ![](https://vedo.embl.es/images/basic/spline_tool.png) 557 """ 558 vtk.vtkContourWidget.__init__(self) 559 560 self.representation = vtk.vtkOrientedGlyphContourRepresentation() 561 self.representation.SetAlwaysOnTop(ontop) 562 563 self.representation.GetLinesProperty().SetColor(get_color(lc)) 564 self.representation.GetLinesProperty().SetLineWidth(lw) 565 566 self.representation.GetProperty().SetColor(get_color(pc)) 567 self.representation.GetProperty().SetPointSize(ps) 568 self.representation.GetProperty().RenderPointsAsSpheresOn() 569 570 self.representation.GetActiveProperty().SetColor(get_color(ac)) 571 self.representation.GetActiveProperty().SetLineWidth(lw + 1) 572 573 self.SetRepresentation(self.representation) 574 575 if utils.is_sequence(points): 576 self.points = Points(points) 577 else: 578 self.points = points 579 580 self.closed = closed 581 582 def add(self, pt): 583 """ 584 Add one point at a specified position in space if 3D, 585 or 2D screen-display position if 2D. 586 """ 587 if len(pt) == 2: 588 self.representation.AddNodeAtDisplayPosition(int(pt[0]), int(pt[1])) 589 else: 590 self.representation.AddNodeAtWorldPosition(pt) 591 return self 592 593 def remove(self, i): 594 """Remove specific node by its index""" 595 self.representation.DeleteNthNode(i) 596 return self 597 598 def on(self): 599 """Activate/Enable the tool""" 600 self.On() 601 self.Render() 602 return self 603 604 def off(self): 605 """Disactivate/Disable the tool""" 606 self.Off() 607 self.Render() 608 return self 609 610 def render(self): 611 """Render the spline""" 612 self.Render() 613 return self 614 615 def bounds(self): 616 """Retrieve the bounding box of the spline as [x0,x1, y0,y1, z0,z1]""" 617 return self.GetBounds() 618 619 def spline(self): 620 """Return the vedo.Spline object.""" 621 self.representation.SetClosedLoop(self.closed) 622 self.representation.BuildRepresentation() 623 pd = self.representation.GetContourRepresentationAsPolyData() 624 pts = utils.vtk2numpy(pd.GetPoints().GetData()) 625 ln = vedo.Line(pts, lw=2, c="k") 626 return ln 627 628 def nodes(self, onscreen=False): 629 """Return the current position in space (or on 2D screen-display) of the spline nodes.""" 630 n = self.representation.GetNumberOfNodes() 631 pts = [] 632 for i in range(n): 633 p = [0.0, 0.0, 0.0] 634 if onscreen: 635 self.representation.GetNthNodeDisplayPosition(i, p) 636 else: 637 self.representation.GetNthNodeWorldPosition(i, p) 638 pts.append(p) 639 return np.array(pts) 640 641 642##################################################################### 643class SliderWidget(vtk.vtkSliderWidget): 644 """Helper class for vtkSliderWidget""" 645 646 def __init__(self): 647 vtk.vtkSliderWidget.__init__(self) 648 649 @property 650 def interactor(self): 651 return self.GetInteractor() 652 653 @interactor.setter 654 def interactor(self, iren): 655 self.SetInteractor(iren) 656 657 @property 658 def representation(self): 659 return self.GetRepresentation() 660 661 @property 662 def value(self): 663 return self.GetRepresentation().GetValue() 664 665 @value.setter 666 def value(self, val): 667 self.GetRepresentation().SetValue(val) 668 669 @property 670 def renderer(self): 671 return self.GetCurrentRenderer() 672 673 @renderer.setter 674 def renderer(self, ren): 675 self.SetCurrentRenderer(ren) 676 677 @property 678 def title(self): 679 self.GetRepresentation().GetTitleText() 680 681 @title.setter 682 def title(self, txt): 683 self.GetRepresentation().SetTitleText(str(txt)) 684 685 @property 686 def range(self): 687 xmin = self.GetRepresentation().GetMinimumValue() 688 xmax = self.GetRepresentation().GetMaximumValue() 689 return [xmin, xmax] 690 691 @range.setter 692 def range(self, vals): 693 if vals[0] is not None: 694 self.GetRepresentation().SetMinimumValue(vals[0]) 695 if vals[1] is not None: 696 self.GetRepresentation().SetMaximumValue(vals[1]) 697 698 def on(self): 699 self.EnabledOn() 700 701 def off(self): 702 self.EnabledOff() 703 704 705##################################################################### 706def Goniometer( 707 p1, 708 p2, 709 p3, 710 font="", 711 arc_size=0.4, 712 s=1, 713 italic=0, 714 rotation=0, 715 prefix="", 716 lc="k2", 717 c="white", 718 alpha=1, 719 lw=2, 720 precision=3, 721): 722 """ 723 Build a graphical goniometer to measure the angle formed by 3 points in space. 724 725 Arguments: 726 p1 : (list) 727 first point 3D coordinates. 728 p2 : (list) 729 the vertex point. 730 p3 : (list) 731 the last point defining the angle. 732 font : (str) 733 Font face. Check [available fonts here](https://vedo.embl.es/fonts). 734 arc_size : (float) 735 dimension of the arc wrt the smallest axis. 736 s : (float) 737 size of the text. 738 italic : (float, bool) 739 italic text. 740 rotation : (float) 741 rotation of text in degrees. 742 prefix : (str) 743 append this string to the numeric value of the angle. 744 lc : (list) 745 color of the goniometer lines. 746 c : (str) 747 color of the goniometer angle filling. Set alpha=0 to remove it. 748 alpha : (float) 749 transparency level. 750 lw : (float) 751 line width. 752 precision : (int) 753 number of significant digits. 754 755 Examples: 756 - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py) 757 758 ![](https://vedo.embl.es/images/pyplot/goniometer.png) 759 """ 760 if isinstance(p1, Points): p1 = p1.GetPosition() 761 if isinstance(p2, Points): p2 = p2.GetPosition() 762 if isinstance(p3, Points): p3 = p3.GetPosition() 763 if len(p1)==2: p1=[p1[0], p1[1], 0.0] 764 if len(p2)==2: p2=[p2[0], p2[1], 0.0] 765 if len(p3)==2: p3=[p3[0], p3[1], 0.0] 766 p1, p2, p3 = np.array(p1), np.array(p2), np.array(p3) 767 768 acts = [] 769 ln = shapes.Line([p1, p2, p3], lw=lw, c=lc) 770 acts.append(ln) 771 772 va = utils.versor(p1 - p2) 773 vb = utils.versor(p3 - p2) 774 r = min(utils.mag(p3 - p2), utils.mag(p1 - p2)) * arc_size 775 ptsarc = [] 776 res = 120 777 imed = int(res / 2) 778 for i in range(res + 1): 779 vi = utils.versor(vb * i / res + va * (res - i) / res) 780 if i == imed: 781 vc = np.array(vi) 782 ptsarc.append(p2 + vi * r) 783 arc = shapes.Line(ptsarc).lw(lw).c(lc) 784 acts.append(arc) 785 786 angle = np.arccos(np.dot(va, vb)) * 180 / np.pi 787 788 lb = shapes.Text3D( 789 prefix + utils.precision(angle, precision) + "º", 790 s=r / 12 * s, 791 font=font, 792 italic=italic, 793 justify="center", 794 ) 795 cr = np.cross(va, vb) 796 lb.pos(p2 + vc * r / 1.75).orientation(cr * np.sign(cr[2]), rotation=rotation) 797 lb.c(c).bc("tomato").lighting("off") 798 acts.append(lb) 799 800 if alpha > 0: 801 pts = [p2] + arc.points().tolist() + [p2] 802 msh = Mesh([pts, [list(range(arc.npoints + 2))]], c=lc, alpha=alpha) 803 msh.lighting("off") 804 msh.triangulate() 805 msh.shift(0, 0, -r / 10000) # to resolve 2d conflicts.. 806 acts.append(msh) 807 808 asse = Assembly(acts) 809 return asse 810 811 812def Light(pos, focal_point=(0, 0, 0), angle=180, c=None, intensity=1): 813 """ 814 Generate a source of light placed at `pos` and directed to `focal point`. 815 Returns a `vtkLight` object. 816 817 Arguments: 818 focal_point : (list) 819 focal point, if a `vedo` object is passed then will grab its position. 820 angle : (float) 821 aperture angle of the light source, in degrees 822 c : (color) 823 set the light color 824 intensity : (float) 825 intensity value between 0 and 1. 826 827 Check also: 828 `plotter.Plotter.remove_lights()` 829 830 Examples: 831 - [lights.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/lights.py) 832 833 ![](https://vedo.embl.es/images/basic/lights.png) 834 """ 835 if c is None: 836 try: 837 c = pos.color() 838 except AttributeError: 839 c = "white" 840 841 if isinstance(pos, vedo.Base3DProp): 842 pos = pos.pos() 843 844 if isinstance(focal_point, vedo.Base3DProp): 845 focal_point = focal_point.pos() 846 847 light = vtk.vtkLight() 848 light.SetLightTypeToSceneLight() 849 light.SetPosition(pos) 850 light.SetConeAngle(angle) 851 light.SetFocalPoint(focal_point) 852 light.SetIntensity(intensity) 853 light.SetColor(get_color(c)) 854 return light 855 856 857##################################################################### 858def ScalarBar( 859 obj, 860 title="", 861 pos=(0.8, 0.05), 862 title_yoffset=15, 863 font_size=12, 864 size=(None, None), 865 nlabels=None, 866 c="k", 867 horizontal=False, 868 use_alpha=True, 869 label_format=":6.3g", 870): 871 """ 872 A 2D scalar bar for the specified obj. 873 874 Arguments: 875 pos : (list) 876 fractional x and y position in the 2D window 877 size : (list) 878 size of the scalarbar in pixel units (width, height) 879 nlabels : (int) 880 number of numeric labels to be shown 881 use_alpha : (bool) 882 retain transparency in scalarbar 883 horizontal : (bool) 884 show in horizontal layout 885 886 Examples: 887 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 888 889 ![](https://user-images.githubusercontent.com/32848391/62940174-4bdc7900-bdd3-11e9-9713-e4f3e2fdab63.png) 890 """ 891 892 if isinstance(obj, Points): 893 vtkscalars = obj.inputdata().GetPointData().GetScalars() 894 if vtkscalars is None: 895 vtkscalars = obj.inputdata().GetCellData().GetScalars() 896 if not vtkscalars: 897 return None 898 lut = vtkscalars.GetLookupTable() 899 if not lut: 900 lut = obj.mapper().GetLookupTable() 901 if not lut: 902 return None 903 904 elif isinstance(obj, (Volume, TetMesh)): 905 lut = utils.ctf2lut(obj) 906 907 elif utils.is_sequence(obj) and len(obj) == 2: 908 x = np.linspace(obj[0], obj[1], 256) 909 data = [] 910 for i in range(256): 911 rgb = color_map(i, c, 0, 256) 912 data.append([x[i], rgb]) 913 lut = build_lut(data) 914 915 elif not hasattr(obj, "mapper"): 916 vedo.logger.error(f"in add_scalarbar(): input is invalid {type(obj)}. Skip.") 917 return None 918 919 else: 920 return None 921 922 c = get_color(c) 923 sb = vtk.vtkScalarBarActor() 924 925 # print(sb.GetLabelFormat()) 926 label_format = label_format.replace(":", "%-#") 927 sb.SetLabelFormat(label_format) 928 929 sb.SetLookupTable(lut) 930 sb.SetUseOpacity(use_alpha) 931 sb.SetDrawFrame(0) 932 sb.SetDrawBackground(0) 933 if lut.GetUseBelowRangeColor(): 934 sb.DrawBelowRangeSwatchOn() 935 sb.SetBelowRangeAnnotation("") 936 if lut.GetUseAboveRangeColor(): 937 sb.DrawAboveRangeSwatchOn() 938 sb.SetAboveRangeAnnotation("") 939 if lut.GetNanColor() != (0.5, 0.0, 0.0, 1.0): 940 sb.DrawNanAnnotationOn() 941 sb.SetNanAnnotation("nan") 942 943 if title: 944 if "\\" in repr(title): 945 for r in shapes._reps: 946 title = title.replace(r[0], r[1]) 947 titprop = sb.GetTitleTextProperty() 948 titprop.BoldOn() 949 titprop.ItalicOff() 950 titprop.ShadowOff() 951 titprop.SetColor(c) 952 titprop.SetVerticalJustificationToTop() 953 titprop.SetFontSize(font_size) 954 titprop.SetFontFamily(vtk.VTK_FONT_FILE) 955 titprop.SetFontFile(utils.get_font_path(settings.default_font)) 956 sb.SetTitle(title) 957 sb.SetVerticalTitleSeparation(title_yoffset) 958 sb.SetTitleTextProperty(titprop) 959 960 sb.UnconstrainedFontSizeOn() 961 sb.DrawAnnotationsOn() 962 sb.DrawTickLabelsOn() 963 sb.SetMaximumNumberOfColors(256) 964 965 if horizontal: 966 sb.SetOrientationToHorizontal() 967 sb.SetNumberOfLabels(3) 968 sb.SetTextPositionToSucceedScalarBar() 969 sb.SetPosition(pos) 970 sb.SetMaximumWidthInPixels(1000) 971 sb.SetMaximumHeightInPixels(50) 972 else: 973 sb.SetNumberOfLabels(7) 974 sb.SetTextPositionToPrecedeScalarBar() 975 sb.SetPosition(pos[0] + 0.09, pos[1]) 976 sb.SetMaximumWidthInPixels(60) 977 sb.SetMaximumHeightInPixels(250) 978 979 if size[0] is not None: 980 sb.SetMaximumWidthInPixels(size[0]) 981 if size[1] is not None: 982 sb.SetMaximumHeightInPixels(size[1]) 983 984 if nlabels is not None: 985 sb.SetNumberOfLabels(nlabels) 986 987 sctxt = sb.GetLabelTextProperty() 988 sctxt.SetFontFamily(vtk.VTK_FONT_FILE) 989 sctxt.SetFontFile(utils.get_font_path(settings.default_font)) 990 sctxt.SetColor(c) 991 sctxt.SetShadow(0) 992 sctxt.SetFontSize(font_size - 2) 993 sb.SetAnnotationTextProperty(sctxt) 994 sb.PickableOff() 995 return sb 996 997 998##################################################################### 999def ScalarBar3D( 1000 obj, 1001 title="", 1002 pos=None, 1003 size=(None, None), 1004 title_font="", 1005 title_xoffset=-1.5, 1006 title_yoffset=0.0, 1007 title_size=1.5, 1008 title_rotation=0.0, 1009 nlabels=8, 1010 label_font="", 1011 label_size=1, 1012 label_offset=0.375, 1013 label_rotation=0, 1014 label_format="", 1015 italic=0, 1016 c=None, 1017 draw_box=True, 1018 above_text=None, 1019 below_text=None, 1020 nan_text="NaN", 1021 categories=None, 1022): 1023 """ 1024 Create a 3D scalar bar for the specified object. 1025 1026 Input `obj` input can be: 1027 1028 - a list of numbers, 1029 - a list of two numbers in the form (min, max), 1030 - a Mesh already containing a set of scalars associated to vertices or cells, 1031 - if None the last object in the list of actors will be used. 1032 1033 Arguments: 1034 size : (list) 1035 (thickness, length) of scalarbar 1036 title : (str) 1037 scalar bar title 1038 title_xoffset : (float) 1039 horizontal space btw title and color scalarbar 1040 title_yoffset : (float) 1041 vertical space offset 1042 title_size : (float) 1043 size of title wrt numeric labels 1044 title_rotation : (float) 1045 title rotation in degrees 1046 nlabels : (int) 1047 number of numeric labels 1048 label_font : (str) 1049 font type for labels 1050 label_size : (float) 1051 label scale factor 1052 label_offset : (float) 1053 space btw numeric labels and scale 1054 label_rotation : (float) 1055 label rotation in degrees 1056 draw_box : (bool) 1057 draw a box around the colorbar 1058 categories : (list) 1059 make a categorical scalarbar, 1060 the input list will have the format [value, color, alpha, textlabel] 1061 1062 Examples: 1063 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 1064 """ 1065 1066 if isinstance(obj, Points): 1067 lut = obj.mapper().GetLookupTable() 1068 if not lut or lut.GetTable().GetNumberOfTuples() == 0: 1069 # create the most similar to the default 1070 obj.cmap("jet_r") 1071 lut = obj.mapper().GetLookupTable() 1072 vmin, vmax = lut.GetRange() 1073 1074 elif isinstance(obj, (Volume, TetMesh)): 1075 lut = utils.ctf2lut(obj) 1076 vmin, vmax = lut.GetRange() 1077 1078 elif utils.is_sequence(obj): 1079 vmin, vmax = np.min(obj), np.max(obj) 1080 1081 else: 1082 vedo.logger.error("in ScalarBar3D(): input must be a vedo object with bounds.") 1083 return obj 1084 1085 bns = obj.bounds() 1086 sx, sy = size 1087 if sy is None: 1088 sy = bns[3] - bns[2] 1089 if sx is None: 1090 sx = sy / 18 1091 1092 if categories is not None: ################################ 1093 ncats = len(categories) 1094 scale = shapes.Grid([-float(sx) * label_offset, 0, 0], 1095 c=c, alpha=1, s=(sx, sy), res=(1, ncats)) 1096 cols, alphas = [], [] 1097 ticks_pos, ticks_txt = [0.0], [""] 1098 for i, cat in enumerate(categories): 1099 cl = get_color(cat[1]) 1100 cols.append(cl) 1101 if len(cat) > 2: 1102 alphas.append(cat[2]) 1103 else: 1104 alphas.append(1) 1105 if len(cat) > 3: 1106 ticks_txt.append(cat[3]) 1107 else: 1108 ticks_txt.append("") 1109 ticks_pos.append((i + 0.5) / ncats) 1110 ticks_pos.append(1.0) 1111 ticks_txt.append("") 1112 rgba = np.c_[np.array(cols) * 255, np.array(alphas) * 255] 1113 scale.cell_individual_colors(rgba) 1114 1115 else: ######################################################## 1116 1117 # build the color scale part 1118 scale = shapes.Grid( 1119 [-float(sx) * label_offset, 0, 0], 1120 c=c, 1121 alpha=1, 1122 s=(sx, sy), 1123 res=(1, lut.GetTable().GetNumberOfTuples()), 1124 ) 1125 cscals = np.linspace(vmin, vmax, lut.GetTable().GetNumberOfTuples()) 1126 1127 if lut.GetScale(): # logarithmic scale 1128 lut10 = vtk.vtkLookupTable() 1129 lut10.DeepCopy(lut) 1130 lut10.SetScaleToLinear() 1131 scale.cmap(lut10, cscals, on="cells") 1132 tk = utils.make_ticks(vmin, vmax, nlabels, logscale=True, useformat=label_format) 1133 else: 1134 scale.cmap(lut, cscals, on="cells") 1135 tk = utils.make_ticks(vmin, vmax, nlabels, logscale=False, useformat=label_format) 1136 ticks_pos, ticks_txt = tk 1137 scale.lw(0).wireframe(False).lighting("off") 1138 1139 scales = [scale] 1140 1141 xbns = scale.xbounds() 1142 if pos is None: 1143 d = sx / 2 1144 if title: 1145 d = np.sqrt((bns[1] - bns[0]) ** 2 + sy * sy) / 20 1146 pos = (bns[1] - xbns[0] + d, (bns[2] + bns[3]) / 2, bns[4]) 1147 1148 lsize = sy / 60 * label_size 1149 1150 tacts = [] 1151 for i, p in enumerate(ticks_pos): 1152 tx = ticks_txt[i] 1153 if i and tx: 1154 # build numeric text 1155 y = (p - 0.5) * sy 1156 if label_rotation: 1157 a = shapes.Text3D( 1158 tx, 1159 pos=[sx * label_offset, y, 0], 1160 s=lsize, 1161 justify="center-top", 1162 c=c, 1163 italic=italic, 1164 font=label_font, 1165 ) 1166 a.RotateZ(label_rotation) 1167 else: 1168 a = shapes.Text3D( 1169 tx, 1170 pos=[sx * label_offset, y, 0], 1171 s=lsize, 1172 justify="center-left", 1173 c=c, 1174 italic=italic, 1175 font=label_font, 1176 ) 1177 1178 tacts.append(a) 1179 1180 # build ticks 1181 tic = shapes.Line([xbns[1], y, 0], [xbns[1] + sx * label_offset / 4, y, 0], lw=2, c=c) 1182 tacts.append(tic) 1183 1184 # build title 1185 if title: 1186 t = shapes.Text3D( 1187 title, 1188 (0, 0, 0), 1189 s=sy / 50 * title_size, 1190 c=c, 1191 justify="centered", 1192 italic=italic, 1193 font=title_font, 1194 ) 1195 t.RotateZ(90 + title_rotation) 1196 t.pos(sx * title_xoffset, title_yoffset, 0) 1197 tacts.append(t) 1198 1199 # build below scale 1200 if lut.GetUseBelowRangeColor(): 1201 r, g, b, alfa = lut.GetBelowRangeColor() 1202 sx = float(sx) 1203 sy = float(sy) 1204 brect = shapes.Rectangle( 1205 [-sx * label_offset - sx / 2, -sy / 2 - sx - sx * 0.1, 0], 1206 [-sx * label_offset + sx / 2, -sy / 2 - sx * 0.1, 0], 1207 c=(r, g, b), 1208 alpha=alfa, 1209 ) 1210 brect.lw(1).lc(c).lighting("off") 1211 scales += [brect] 1212 if below_text is None: 1213 below_text = " <" + str(vmin) 1214 if below_text: 1215 if label_rotation: 1216 btx = shapes.Text3D( 1217 below_text, 1218 (0, 0, 0), 1219 s=lsize, 1220 c=c, 1221 justify="center-top", 1222 italic=italic, 1223 font=label_font, 1224 ) 1225 btx.RotateZ(label_rotation) 1226 else: 1227 btx = shapes.Text3D( 1228 below_text, 1229 (0, 0, 0), 1230 s=lsize, 1231 c=c, 1232 justify="center-left", 1233 italic=italic, 1234 font=label_font, 1235 ) 1236 1237 btx.pos(sx * label_offset, -sy / 2 - sx * 0.66, 0) 1238 tacts.append(btx) 1239 1240 # build above scale 1241 if lut.GetUseAboveRangeColor(): 1242 r, g, b, alfa = lut.GetAboveRangeColor() 1243 arect = shapes.Rectangle( 1244 [-sx * label_offset - sx / 2, sy / 2 + sx * 0.1, 0], 1245 [-sx * label_offset + sx / 2, sy / 2 + sx + sx * 0.1, 0], 1246 c=(r, g, b), 1247 alpha=alfa, 1248 ) 1249 arect.lw(1).lc(c).lighting("off") 1250 scales += [arect] 1251 if above_text is None: 1252 above_text = " >" + str(vmax) 1253 if above_text: 1254 if label_rotation: 1255 atx = shapes.Text3D( 1256 above_text, 1257 (0, 0, 0), 1258 s=lsize, 1259 c=c, 1260 justify="center-top", 1261 italic=italic, 1262 font=label_font, 1263 ) 1264 atx.RotateZ(label_rotation) 1265 else: 1266 atx = shapes.Text3D( 1267 above_text, 1268 (0, 0, 0), 1269 s=lsize, 1270 c=c, 1271 justify="center-left", 1272 italic=italic, 1273 font=label_font, 1274 ) 1275 1276 atx.pos(sx * label_offset, sy / 2 + sx * 0.66, 0) 1277 tacts.append(atx) 1278 1279 # build NaN scale 1280 if lut.GetNanColor() != (0.5, 0.0, 0.0, 1.0): 1281 nanshift = sx * 0.1 1282 if brect: 1283 nanshift += sx 1284 r, g, b, alfa = lut.GetNanColor() 1285 nanrect = shapes.Rectangle( 1286 [-sx * label_offset - sx / 2, -sy / 2 - sx - sx * 0.1 - nanshift, 0], 1287 [-sx * label_offset + sx / 2, -sy / 2 - sx * 0.1 - nanshift, 0], 1288 c=(r, g, b), 1289 alpha=alfa, 1290 ) 1291 nanrect.lw(1).lc(c).lighting("off") 1292 scales += [nanrect] 1293 if label_rotation: 1294 nantx = shapes.Text3D( 1295 nan_text, 1296 (0, 0, 0), 1297 s=lsize, 1298 c=c, 1299 justify="center-left", 1300 italic=italic, 1301 font=label_font, 1302 ) 1303 nantx.RotateZ(label_rotation) 1304 else: 1305 nantx = shapes.Text3D( 1306 nan_text, 1307 (0, 0, 0), 1308 s=lsize, 1309 c=c, 1310 justify="center-left", 1311 italic=italic, 1312 font=label_font, 1313 ) 1314 nantx.pos(sx * label_offset, -sy / 2 - sx * 0.66 - nanshift, 0) 1315 tacts.append(nantx) 1316 1317 if draw_box: 1318 tacts.append(scale.box().lw(1)) 1319 1320 for a in tacts: 1321 a.PickableOff() 1322 1323 mtacts = merge(tacts).lighting("off") 1324 mtacts.PickableOff() 1325 scale.PickableOff() 1326 1327 sact = Assembly(scales + tacts) 1328 sact.SetPosition(pos) 1329 sact.PickableOff() 1330 sact.UseBoundsOff() 1331 sact.name = "ScalarBar3D" 1332 return sact 1333 1334 1335##################################################################### 1336class Slider2D(SliderWidget): 1337 """ 1338 Add a slider which can call an external custom function. 1339 """ 1340 def __init__( 1341 self, 1342 sliderfunc, 1343 xmin, 1344 xmax, 1345 value=None, 1346 pos=4, 1347 title="", 1348 font="Calco", 1349 title_size=1, 1350 c="k", 1351 alpha=1, 1352 show_value=True, 1353 delayed=False, 1354 **options, 1355 ): 1356 """ 1357 Add a slider which can call an external custom function. 1358 Set any value as float to increase the number of significant digits above the slider. 1359 1360 Use `play()` to start an animation between the current slider value and the last value. 1361 1362 Arguments: 1363 sliderfunc : (function) 1364 external function to be called by the widget 1365 xmin : (float) 1366 lower value of the slider 1367 xmax : (float) 1368 upper value 1369 value : (float) 1370 current value 1371 pos : (list, str) 1372 position corner number: horizontal [1-5] or vertical [11-15] 1373 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1374 and also by a string descriptor (eg. "bottom-left") 1375 title : (str) 1376 title text 1377 font : (str) 1378 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1379 title_size : (float) 1380 title text scale [1.0] 1381 show_value : (bool) 1382 if True current value is shown 1383 delayed : (bool) 1384 if True the callback is delayed until when the mouse button is released 1385 alpha : (float) 1386 opacity of the scalar bar texts 1387 slider_length : (float) 1388 slider length 1389 slider_width : (float) 1390 slider width 1391 end_cap_length : (float) 1392 length of the end cap 1393 end_cap_width : (float) 1394 width of the end cap 1395 tube_width : (float) 1396 width of the tube 1397 title_height : (float) 1398 height of the title 1399 tformat : (str) 1400 format of the title 1401 1402 Examples: 1403 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1404 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1405 1406 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1407 """ 1408 slider_length = options.pop("slider_length", 0.015) 1409 slider_width = options.pop("slider_width", 0.025) 1410 end_cap_length= options.pop("end_cap_length", 0.0015) 1411 end_cap_width = options.pop("end_cap_width", 0.0125) 1412 tube_width = options.pop("tube_width", 0.0075) 1413 title_height = options.pop("title_height", 0.025) 1414 if options: 1415 vedo.logger.warning(f"in Slider2D unknown option(s): {options}") 1416 1417 c = get_color(c) 1418 1419 if value is None or value < xmin: 1420 value = xmin 1421 1422 slider_rep = vtk.vtkSliderRepresentation2D() 1423 slider_rep.SetMinimumValue(xmin) 1424 slider_rep.SetMaximumValue(xmax) 1425 slider_rep.SetValue(value) 1426 slider_rep.SetSliderLength(slider_length) 1427 slider_rep.SetSliderWidth(slider_width) 1428 slider_rep.SetEndCapLength(end_cap_length) 1429 slider_rep.SetEndCapWidth(end_cap_width) 1430 slider_rep.SetTubeWidth(tube_width) 1431 slider_rep.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay() 1432 slider_rep.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay() 1433 1434 if isinstance(pos, str): 1435 if "top" in pos: 1436 if "left" in pos: 1437 if "vert" in pos: 1438 pos = 11 1439 else: 1440 pos = 1 1441 elif "right" in pos: 1442 if "vert" in pos: 1443 pos = 12 1444 else: 1445 pos = 2 1446 elif "bott" in pos: 1447 if "left" in pos: 1448 if "vert" in pos: 1449 pos = 13 1450 else: 1451 pos = 3 1452 elif "right" in pos: 1453 if "vert" in pos: 1454 if "span" in pos: 1455 pos = 15 1456 else: 1457 pos = 14 1458 else: 1459 pos = 4 1460 elif "span" in pos: 1461 pos = 5 1462 1463 if utils.is_sequence(pos): 1464 slider_rep.GetPoint1Coordinate().SetValue(pos[0][0], pos[0][1]) 1465 slider_rep.GetPoint2Coordinate().SetValue(pos[1][0], pos[1][1]) 1466 elif pos == 1: # top-left horizontal 1467 slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.93) 1468 slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.93) 1469 elif pos == 2: 1470 slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.93) 1471 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.93) 1472 elif pos == 3: 1473 slider_rep.GetPoint1Coordinate().SetValue(0.05, 0.06) 1474 slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.06) 1475 elif pos == 4: # bottom-right 1476 slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06) 1477 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06) 1478 elif pos == 5: # bottom span horizontal 1479 slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.06) 1480 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06) 1481 elif pos == 11: # top-left vertical 1482 slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.54) 1483 slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.9) 1484 elif pos == 12: 1485 slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.54) 1486 slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.9) 1487 elif pos == 13: 1488 slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.1) 1489 slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.54) 1490 elif pos == 14: # bottom-right vertical 1491 slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.1) 1492 slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.54) 1493 elif pos == 15: # right margin vertical 1494 slider_rep.GetPoint1Coordinate().SetValue(0.95, 0.1) 1495 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.9) 1496 else: # bottom-right 1497 slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06) 1498 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06) 1499 1500 if show_value: 1501 if isinstance(xmin, int) and isinstance(xmax, int) and isinstance(value, int): 1502 frm = "%0.0f" 1503 else: 1504 frm = "%0.2f" 1505 1506 frm = options.pop("tformat", frm) 1507 1508 slider_rep.SetLabelFormat(frm) # default is '%0.3g' 1509 slider_rep.GetLabelProperty().SetShadow(0) 1510 slider_rep.GetLabelProperty().SetBold(0) 1511 slider_rep.GetLabelProperty().SetOpacity(alpha) 1512 slider_rep.GetLabelProperty().SetColor(c) 1513 if isinstance(pos, int) and pos > 10: 1514 slider_rep.GetLabelProperty().SetOrientation(90) 1515 else: 1516 slider_rep.ShowSliderLabelOff() 1517 slider_rep.GetTubeProperty().SetColor(c) 1518 slider_rep.GetTubeProperty().SetOpacity(0.75) 1519 slider_rep.GetSliderProperty().SetColor(c) 1520 slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c))) 1521 slider_rep.GetCapProperty().SetColor(c) 1522 1523 slider_rep.SetTitleHeight(title_height * title_size) 1524 slider_rep.GetTitleProperty().SetShadow(0) 1525 slider_rep.GetTitleProperty().SetColor(c) 1526 slider_rep.GetTitleProperty().SetOpacity(alpha) 1527 slider_rep.GetTitleProperty().SetBold(0) 1528 if font.lower() == "courier": 1529 slider_rep.GetTitleProperty().SetFontFamilyToCourier() 1530 elif font.lower() == "times": 1531 slider_rep.GetTitleProperty().SetFontFamilyToTimes() 1532 elif font.lower() == "arial": 1533 slider_rep.GetTitleProperty().SetFontFamilyToArial() 1534 else: 1535 if font == "": 1536 font = utils.get_font_path(settings.default_font) 1537 else: 1538 font = utils.get_font_path(font) 1539 slider_rep.GetTitleProperty().SetFontFamily(vtk.VTK_FONT_FILE) 1540 slider_rep.GetLabelProperty().SetFontFamily(vtk.VTK_FONT_FILE) 1541 slider_rep.GetTitleProperty().SetFontFile(font) 1542 slider_rep.GetLabelProperty().SetFontFile(font) 1543 1544 if title: 1545 slider_rep.SetTitleText(title) 1546 if not utils.is_sequence(pos): 1547 if isinstance(pos, int) and pos > 10: 1548 slider_rep.GetTitleProperty().SetOrientation(90) 1549 else: 1550 if abs(pos[0][0] - pos[1][0]) < 0.1: 1551 slider_rep.GetTitleProperty().SetOrientation(90) 1552 1553 SliderWidget.__init__(self) 1554 1555 self.SetAnimationModeToJump() 1556 self.SetRepresentation(slider_rep) 1557 if delayed: 1558 self.AddObserver("EndInteractionEvent", sliderfunc) 1559 else: 1560 self.AddObserver("InteractionEvent", sliderfunc) 1561 1562 1563##################################################################### 1564class Slider3D(SliderWidget): 1565 """ 1566 Add a 3D slider which can call an external custom function. 1567 """ 1568 1569 def __init__( 1570 self, 1571 sliderfunc, 1572 pos1, 1573 pos2, 1574 xmin, 1575 xmax, 1576 value=None, 1577 s=0.03, 1578 t=1, 1579 title="", 1580 rotation=0, 1581 c=None, 1582 show_value=True, 1583 ): 1584 """ 1585 Add a 3D slider which can call an external custom function. 1586 1587 Arguments: 1588 sliderfunc : (function) 1589 external function to be called by the widget 1590 pos1 : (list) 1591 first position 3D coordinates 1592 pos2 : (list) 1593 second position 3D coordinates 1594 xmin : (float) 1595 lower value 1596 xmax : (float) 1597 upper value 1598 value : (float) 1599 initial value 1600 s : (float) 1601 label scaling factor 1602 t : (float) 1603 tube scaling factor 1604 title : (str) 1605 title text 1606 c : (color) 1607 slider color 1608 rotation : (float) 1609 title rotation around slider axis 1610 show_value : (bool) 1611 if True current value is shown on top of the slider 1612 1613 Examples: 1614 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1615 """ 1616 c = get_color(c) 1617 1618 if value is None or value < xmin: 1619 value = xmin 1620 1621 slider_rep = vtk.vtkSliderRepresentation3D() 1622 slider_rep.SetMinimumValue(xmin) 1623 slider_rep.SetMaximumValue(xmax) 1624 slider_rep.SetValue(value) 1625 1626 slider_rep.GetPoint1Coordinate().SetCoordinateSystemToWorld() 1627 slider_rep.GetPoint2Coordinate().SetCoordinateSystemToWorld() 1628 slider_rep.GetPoint1Coordinate().SetValue(pos2) 1629 slider_rep.GetPoint2Coordinate().SetValue(pos1) 1630 1631 # slider_rep.SetPoint1InWorldCoordinates(pos2[0], pos2[1], pos2[2]) 1632 # slider_rep.SetPoint2InWorldCoordinates(pos1[0], pos1[1], pos1[2]) 1633 1634 slider_rep.SetSliderWidth(0.03 * t) 1635 slider_rep.SetTubeWidth(0.01 * t) 1636 slider_rep.SetSliderLength(0.04 * t) 1637 slider_rep.SetSliderShapeToCylinder() 1638 slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c))) 1639 slider_rep.GetSliderProperty().SetColor(np.array(c) / 1.5) 1640 slider_rep.GetCapProperty().SetOpacity(0) 1641 slider_rep.SetRotation(rotation) 1642 1643 if not show_value: 1644 slider_rep.ShowSliderLabelOff() 1645 1646 slider_rep.SetTitleText(title) 1647 slider_rep.SetTitleHeight(s * t) 1648 slider_rep.SetLabelHeight(s * t * 0.85) 1649 1650 slider_rep.GetTubeProperty().SetColor(c) 1651 1652 SliderWidget.__init__(self) 1653 1654 self.SetRepresentation(slider_rep) 1655 self.SetAnimationModeToJump() 1656 self.AddObserver("InteractionEvent", sliderfunc) 1657 1658class BaseCutter: 1659 """ 1660 Base class for Cutter widgets. 1661 """ 1662 def __init__(self): 1663 self._implicit_func = None 1664 self.widget = None 1665 self.clipper=None 1666 self.cutter=None 1667 self._alpha = 0.5 1668 self._keypress_id = None 1669 1670 def invert(self): 1671 """Invert selection.""" 1672 self.clipper.SetInsideOut(not self.clipper.GetInsideOut()) 1673 return self 1674 1675 def bounds(self, value=None): 1676 """Set or get the bounding box.""" 1677 if value is None: 1678 return self.cutter.GetBounds() 1679 else: 1680 self._implicit_func.SetBounds(value) 1681 return self 1682 1683 def on(self): 1684 """Switch the widget on or off.""" 1685 self.widget.On() 1686 return self 1687 1688 def off(self): 1689 """Switch the widget on or off.""" 1690 self.widget.Off() 1691 return self 1692 1693 def add_to(self, plt): 1694 """Assign the widget to the provided `Plotter` instance.""" 1695 self.widget.SetInteractor(plt.interactor) 1696 self.widget.SetCurrentRenderer(plt.renderer) 1697 if self.widget not in plt.widgets: 1698 plt.widgets.append(self.widget) 1699 1700 cpoly = self.clipper.GetOutput() 1701 self.mesh._update(cpoly) 1702 1703 out = self.clipper.GetClippedOutputPort() 1704 self.remnant.mapper().SetInputConnection(out) 1705 self.remnant.alpha(self._alpha).color((0.5, 0.5, 0.5)) 1706 self.remnant.lighting('off').wireframe() 1707 plt.add(self.remnant) 1708 self._keypress_id = plt.interactor.AddObserver("KeyPressEvent", self._keypress) 1709 if plt.interactor and plt.interactor.GetInitialized(): 1710 self.widget.On() 1711 self._select_polygons(self.widget, "InteractionEvent") 1712 plt.interactor.Render() 1713 return self 1714 1715 def remove_from(self, plt): 1716 """Remove the widget to the provided `Plotter` instance.""" 1717 self.widget.Off() 1718 self.RemoveAllObservers() 1719 plt.remove(self.remnant) 1720 if self.widget in plt.widgets: 1721 plt.widgets.remove(self.widget) 1722 if self._keypress_id: 1723 plt.interactor.RemoveObserver(self._keypress_id) 1724 return self 1725 1726 1727class PlaneCutter(vtk.vtkPlaneWidget, BaseCutter): 1728 """ 1729 Create a box widget to cut away parts of a Mesh. 1730 """ 1731 def __init__( 1732 self, 1733 mesh, 1734 invert=False, 1735 can_translate=True, 1736 can_scale=True, 1737 c=(0.25, 0.25, 0.25), 1738 origin=(), 1739 normal=(), 1740 padding=0.05, 1741 alpha=0.05, 1742 ): 1743 """ 1744 Create a box widget to cut away parts of a Mesh. 1745 1746 Arguments: 1747 mesh : (Mesh) 1748 the input mesh 1749 invert : (bool) 1750 invert the clipping plane 1751 can_translate : (bool) 1752 enable translation of the widget 1753 can_scale : (bool) 1754 enable scaling of the widget 1755 origin : (list) 1756 origin of the plane 1757 normal : (list) 1758 normal to the plane 1759 padding : (float) 1760 padding around the input mesh 1761 c : (color) 1762 color of the box cutter widget 1763 alpha : (float) 1764 transparency of the cut-off part of the input mesh 1765 """ 1766 super().__init__() 1767 1768 self.mesh = mesh 1769 self.remnant = Mesh() 1770 self.remnant.name = mesh.name + "Remnant" 1771 self.remnant.pickable(False) 1772 1773 self._alpha = alpha 1774 self._keypress_id = None 1775 1776 self._implicit_func = vtk.vtkPlane() 1777 1778 poly = mesh.polydata() 1779 self.clipper = vtk.vtkClipPolyData() 1780 self.clipper.GenerateClipScalarsOff() 1781 self.clipper.SetInputData(poly) 1782 self.clipper.SetClipFunction(self._implicit_func) 1783 self.clipper.SetInsideOut(invert) 1784 self.clipper.GenerateClippedOutputOn() 1785 self.clipper.Update() 1786 1787 self.widget = vtk.vtkImplicitPlaneWidget() 1788 1789 # self.widget.KeyPressActivationOff() 1790 # self.widget.SetKeyPressActivationValue('i') 1791 1792 self.widget.SetOriginTranslation(can_translate) 1793 self.widget.SetOutlineTranslation(can_translate) 1794 self.widget.SetScaleEnabled(can_scale) 1795 1796 self.widget.GetOutlineProperty().SetColor(c) 1797 self.widget.GetOutlineProperty().SetOpacity(0.25) 1798 self.widget.GetOutlineProperty().SetLineWidth(1) 1799 self.widget.GetOutlineProperty().LightingOff() 1800 1801 self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3")) 1802 1803 self.widget.SetTubing(0) 1804 self.widget.SetDrawPlane(1) 1805 self.widget.GetPlaneProperty().LightingOff() 1806 self.widget.GetPlaneProperty().SetOpacity(0.05) 1807 self.widget.GetSelectedPlaneProperty().SetColor(get_color("red5")) 1808 self.widget.GetSelectedPlaneProperty().LightingOff() 1809 1810 self.widget.SetPlaceFactor(1.0 + padding) 1811 self.widget.SetInputData(poly) 1812 self.widget.PlaceWidget() 1813 self.widget.AddObserver("InteractionEvent", self._select_polygons) 1814 1815 if len(origin) == 3: 1816 self.widget.SetOrigin(origin) 1817 else: 1818 self.widget.SetOrigin(mesh.center_of_mass()) 1819 1820 if len(normal) == 3: 1821 self.widget.SetNormal(normal) 1822 else: 1823 self.widget.SetNormal((1, 0, 0)) 1824 1825 1826 def _select_polygons(self, vobj, event): 1827 vobj.GetPlane(self._implicit_func) 1828 1829 def _keypress(self, vobj, event): 1830 if vobj.GetKeySym() == "r": # reset planes 1831 self.widget.GetPlane(self._implicit_func) 1832 self.widget.PlaceWidget() 1833 self.widget.GetInteractor().Render() 1834 elif vobj.GetKeySym() == "u": # invert cut 1835 self.invert() 1836 self.widget.GetInteractor().Render() 1837 elif vobj.GetKeySym() == "x": # set normal along x 1838 self.widget.SetNormal((1, 0, 0)) 1839 self.widget.GetPlane(self._implicit_func) 1840 self.widget.PlaceWidget() 1841 self.widget.GetInteractor().Render() 1842 elif vobj.GetKeySym() == "y": # set normal along y 1843 self.widget.SetNormal((0, 1, 0)) 1844 self.widget.GetPlane(self._implicit_func) 1845 self.widget.PlaceWidget() 1846 self.widget.GetInteractor().Render() 1847 elif vobj.GetKeySym() == "z": # set normal along z 1848 self.widget.SetNormal((0, 0, 1)) 1849 self.widget.GetPlane(self._implicit_func) 1850 self.widget.PlaceWidget() 1851 self.widget.GetInteractor().Render() 1852 elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh 1853 if self.widget.GetInteractor(): 1854 if self.widget.GetInteractor().GetControlKey(): 1855 printc(":save: saving mesh to vedo_clipped.vtk") 1856 self.mesh.write("vedo_clipped.vtk") 1857 1858 1859class BoxCutter(vtk.vtkBoxWidget, BaseCutter): 1860 """ 1861 Create a box widget to cut away parts of a Mesh. 1862 """ 1863 def __init__( 1864 self, 1865 mesh, 1866 invert=False, 1867 can_rotate=True, 1868 can_translate=True, 1869 can_scale=True, 1870 initial_bounds=(), 1871 padding=0.025, 1872 c=(0.25, 0.25, 0.25), 1873 alpha=0.05, 1874 ): 1875 """ 1876 Create a box widget to cut away parts of a Mesh. 1877 1878 Arguments: 1879 mesh : (Mesh) 1880 the input mesh 1881 invert : (bool) 1882 invert the clipping plane 1883 can_rotate : (bool) 1884 enable rotation of the widget 1885 can_translate : (bool) 1886 enable translation of the widget 1887 can_scale : (bool) 1888 enable scaling of the widget 1889 initial_bounds : (list) 1890 initial bounds of the box widget 1891 c : (color) 1892 color of the box cutter widget 1893 alpha : (float) 1894 transparency of the cut-off part of the input mesh 1895 """ 1896 super().__init__() 1897 1898 self.mesh = mesh 1899 self.remnant = Mesh() 1900 self.remnant.name = mesh.name + "Remnant" 1901 self.remnant.pickable(False) 1902 1903 self._alpha = alpha 1904 self._keypress_id = None 1905 self._init_bounds = initial_bounds 1906 if len(self._init_bounds) == 0: 1907 self._init_bounds = mesh.bounds() 1908 else: 1909 self._init_bounds = initial_bounds 1910 1911 self._implicit_func = vtk.vtkPlanes() 1912 self._implicit_func.SetBounds(self._init_bounds) 1913 1914 poly = mesh.polydata() 1915 self.clipper = vtk.vtkClipPolyData() 1916 self.clipper.GenerateClipScalarsOff() 1917 self.clipper.SetInputData(poly) 1918 self.clipper.SetClipFunction(self._implicit_func) 1919 self.clipper.SetInsideOut(not invert) 1920 self.clipper.GenerateClippedOutputOn() 1921 self.clipper.Update() 1922 1923 self.widget = vtk.vtkBoxWidget() 1924 1925 self.widget.SetRotationEnabled(can_rotate) 1926 self.widget.SetTranslationEnabled(can_translate) 1927 self.widget.SetScalingEnabled(can_scale) 1928 1929 self.widget.OutlineCursorWiresOn() 1930 self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3")) 1931 self.widget.GetSelectedHandleProperty().SetColor(get_color("red5")) 1932 1933 self.widget.GetOutlineProperty().SetColor(c) 1934 self.widget.GetOutlineProperty().SetOpacity(1) 1935 self.widget.GetOutlineProperty().SetLineWidth(1) 1936 self.widget.GetOutlineProperty().LightingOff() 1937 1938 self.widget.GetSelectedFaceProperty().LightingOff() 1939 self.widget.GetSelectedFaceProperty().SetOpacity(0.1) 1940 1941 self.widget.SetPlaceFactor(1.0 + padding) 1942 self.widget.SetInputData(poly) 1943 self.widget.PlaceWidget() 1944 self.widget.AddObserver("InteractionEvent", self._select_polygons) 1945 1946 def _select_polygons(self, vobj, event): 1947 vobj.GetPlanes(self._implicit_func) 1948 1949 def _keypress(self, vobj, event): 1950 if vobj.GetKeySym() == "r": # reset planes 1951 self._implicit_func.SetBounds(self._init_bounds) 1952 self.widget.GetPlanes(self._implicit_func) 1953 self.widget.PlaceWidget() 1954 self.widget.GetInteractor().Render() 1955 elif vobj.GetKeySym() == "u": 1956 self.invert() 1957 self.widget.GetInteractor().Render() 1958 elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh 1959 if self.widget.GetInteractor(): 1960 if self.widget.GetInteractor().GetControlKey(): 1961 printc(":save: saving mesh to vedo_clipped.vtk") 1962 self.mesh.write("vedo_clipped.vtk") 1963 1964 1965class SphereCutter(vtk.vtkSphereWidget, BaseCutter): 1966 """ 1967 Create a box widget to cut away parts of a Mesh. 1968 """ 1969 def __init__( 1970 self, 1971 mesh, 1972 invert=False, 1973 can_translate=True, 1974 can_scale=True, 1975 origin=(), 1976 radius=0, 1977 res=60, 1978 c='white', 1979 alpha=0.05, 1980 ): 1981 """ 1982 Create a box widget to cut away parts of a Mesh. 1983 1984 Arguments: 1985 mesh : Mesh 1986 the input mesh 1987 invert : bool 1988 invert the clipping 1989 can_translate : bool 1990 enable translation of the widget 1991 can_scale : bool 1992 enable scaling of the widget 1993 origin : list 1994 initial position of the sphere widget 1995 radius : float 1996 initial radius of the sphere widget 1997 res : int 1998 resolution of the sphere widget 1999 c : color 2000 color of the box cutter widget 2001 alpha : float 2002 transparency of the cut-off part of the input mesh 2003 """ 2004 super().__init__() 2005 2006 self.mesh = mesh 2007 self.remnant = Mesh() 2008 self.remnant.name = mesh.name + "Remnant" 2009 self.remnant.pickable(False) 2010 2011 self._alpha = alpha 2012 self._keypress_id = None 2013 2014 self._implicit_func = vtk.vtkSphere() 2015 2016 if len(origin) == 3: 2017 self._implicit_func.SetCenter(origin) 2018 else: 2019 origin = mesh.center_of_mass() 2020 self._implicit_func.SetCenter(origin) 2021 2022 if radius > 0: 2023 self._implicit_func.SetRadius(radius) 2024 else: 2025 radius = mesh.average_size() * 2 2026 self._implicit_func.SetRadius(radius) 2027 2028 poly = mesh.polydata() 2029 self.clipper = vtk.vtkClipPolyData() 2030 self.clipper.GenerateClipScalarsOff() 2031 self.clipper.SetInputData(poly) 2032 self.clipper.SetClipFunction(self._implicit_func) 2033 self.clipper.SetInsideOut(not invert) 2034 self.clipper.GenerateClippedOutputOn() 2035 self.clipper.Update() 2036 2037 self.widget = vtk.vtkSphereWidget() 2038 2039 self.widget.SetThetaResolution(res*2) 2040 self.widget.SetPhiResolution(res) 2041 self.widget.SetRadius(radius) 2042 self.widget.SetCenter(origin) 2043 self.widget.SetRepresentation(2) 2044 self.widget.HandleVisibilityOff() 2045 2046 self.widget.SetTranslation(can_translate) 2047 self.widget.SetScale(can_scale) 2048 2049 self.widget.HandleVisibilityOff() 2050 self.widget.GetSphereProperty().SetColor(get_color(c)) 2051 self.widget.GetSphereProperty().SetOpacity(0.2) 2052 self.widget.GetSelectedSphereProperty().SetColor(get_color("red5")) 2053 self.widget.GetSelectedSphereProperty().SetOpacity(0.2) 2054 2055 self.widget.SetPlaceFactor(1.0) 2056 self.widget.SetInputData(poly) 2057 self.widget.PlaceWidget() 2058 self.widget.AddObserver("InteractionEvent", self._select_polygons) 2059 2060 def _select_polygons(self, vobj, event): 2061 vobj.GetSphere(self._implicit_func) 2062 2063 def _keypress(self, vobj, event): 2064 if vobj.GetKeySym() == "r": # reset planes 2065 self._implicit_func.SetBounds(self._init_bounds) 2066 self.widget.GetPlanes(self._implicit_func) 2067 self.widget.PlaceWidget() 2068 self.widget.GetInteractor().Render() 2069 elif vobj.GetKeySym() == "u": 2070 self.invert() 2071 self.widget.GetInteractor().Render() 2072 elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh 2073 if self.widget.GetInteractor(): 2074 if self.widget.GetInteractor().GetControlKey(): 2075 printc(":save: saving mesh to vedo_clipped.vtk") 2076 self.mesh.write("vedo_clipped.vtk") 2077 2078 2079##################################################################### 2080class RendererFrame(vtk.vtkActor2D): 2081 """ 2082 Add a line around the renderer subwindow. 2083 """ 2084 2085 def __init__(self, c="k", alpha=None, lw=None, padding=None): 2086 """ 2087 Add a line around the renderer subwindow. 2088 2089 Arguments: 2090 c : (color) 2091 color of the line. 2092 alpha : (float) 2093 opacity. 2094 lw : (int) 2095 line width in pixels. 2096 padding : (int) 2097 padding in pixel units. 2098 """ 2099 2100 if lw is None: 2101 lw = settings.renderer_frame_width 2102 if lw == 0: 2103 return None 2104 2105 if alpha is None: 2106 alpha = settings.renderer_frame_alpha 2107 2108 if padding is None: 2109 padding = settings.renderer_frame_padding 2110 2111 c = get_color(c) 2112 2113 ppoints = vtk.vtkPoints() # Generate the polyline 2114 xy = 1 - padding 2115 psqr = [[padding, padding], [padding, xy], [xy, xy], [xy, padding], [padding, padding]] 2116 for i, pt in enumerate(psqr): 2117 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2118 lines = vtk.vtkCellArray() 2119 lines.InsertNextCell(len(psqr)) 2120 for i in range(len(psqr)): 2121 lines.InsertCellPoint(i) 2122 pd = vtk.vtkPolyData() 2123 pd.SetPoints(ppoints) 2124 pd.SetLines(lines) 2125 2126 mapper = vtk.vtkPolyDataMapper2D() 2127 mapper.SetInputData(pd) 2128 cs = vtk.vtkCoordinate() 2129 cs.SetCoordinateSystemToNormalizedViewport() 2130 mapper.SetTransformCoordinate(cs) 2131 2132 vtk.vtkActor2D.__init__(self) 2133 2134 self.GetPositionCoordinate().SetValue(0, 0) 2135 self.GetPosition2Coordinate().SetValue(1, 1) 2136 self.SetMapper(mapper) 2137 self.GetProperty().SetColor(c) 2138 self.GetProperty().SetOpacity(alpha) 2139 self.GetProperty().SetLineWidth(lw) 2140 2141##################################################################### 2142class ProgressBarWidget(vtk.vtkActor2D): 2143 """ 2144 Add a progress bar in the rendering window. 2145 """ 2146 def __init__(self, n=None, c="blue5", alpha=0.8, lw=10, autohide=True): 2147 """ 2148 Add a progress bar window. 2149 2150 Arguments: 2151 n : (int) 2152 number of iterations. 2153 If None, you need to call `update(fraction)` manually. 2154 c : (color) 2155 color of the line. 2156 alpha : (float) 2157 opacity of the line. 2158 lw : (int) 2159 line width in pixels. 2160 autohide : (bool) 2161 if True, hide the progress bar when completed. 2162 """ 2163 self.n = 0 2164 self.iterations = n 2165 self.autohide = autohide 2166 2167 ppoints = vtk.vtkPoints() # Generate the line 2168 psqr = [[0, 0, 0], [1, 0, 0]] 2169 for i, pt in enumerate(psqr): 2170 ppoints.InsertPoint(i, *pt) 2171 lines = vtk.vtkCellArray() 2172 lines.InsertNextCell(len(psqr)) 2173 for i in range(len(psqr)): 2174 lines.InsertCellPoint(i) 2175 pd = vtk.vtkPolyData() 2176 pd.SetPoints(ppoints) 2177 pd.SetLines(lines) 2178 self._data = pd 2179 2180 mapper = vtk.vtkPolyDataMapper2D() 2181 mapper.SetInputData(pd) 2182 cs = vtk.vtkCoordinate() 2183 cs.SetCoordinateSystemToNormalizedViewport() 2184 mapper.SetTransformCoordinate(cs) 2185 2186 vtk.vtkActor2D.__init__(self) 2187 2188 self.SetMapper(mapper) 2189 self.GetProperty().SetOpacity(alpha) 2190 self.GetProperty().SetColor(get_color(c)) 2191 self.GetProperty().SetLineWidth(lw*2) 2192 2193 2194 def lw(self, value): 2195 """Set width.""" 2196 self.GetProperty().SetLineWidth(value*2) 2197 return self 2198 2199 def c(self, color): 2200 """Set color.""" 2201 c = get_color(color) 2202 self.GetProperty().SetColor(c) 2203 return self 2204 2205 def alpha(self, value): 2206 """Set opacity.""" 2207 self.GetProperty().SetOpacity(value) 2208 return self 2209 2210 def update(self, fraction=None): 2211 """Update progress bar to fraction of the window width.""" 2212 if fraction is None: 2213 if self.iterations is None: 2214 vedo.printc("Error in ProgressBarWindow: must specify iterations", c='r') 2215 return self 2216 self.n += 1 2217 fraction = self.n / self.iterations 2218 2219 if fraction >= 1 and self.autohide: 2220 fraction = 0 2221 2222 psqr = [[0, 0, 0], [fraction, 0, 0]] 2223 vpts = utils.numpy2vtk(psqr, dtype=np.float32) 2224 self._data.GetPoints().SetData(vpts) 2225 return self 2226 2227 def reset(self): 2228 """Reset progress bar.""" 2229 self.n = 0 2230 self.update(0) 2231 return self 2232 2233 2234##################################################################### 2235class Icon(vtk.vtkOrientationMarkerWidget): 2236 """ 2237 Add an inset icon mesh into the renderer. 2238 """ 2239 2240 def __init__(self, mesh, pos=3, size=0.08): 2241 """ 2242 Arguments: 2243 pos : (list, int) 2244 icon position in the range [1-4] indicating one of the 4 corners, 2245 or it can be a tuple (x,y) as a fraction of the renderer size. 2246 size : (float) 2247 size of the icon space as fraction of the window size. 2248 2249 Examples: 2250 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 2251 """ 2252 vtk.vtkOrientationMarkerWidget.__init__(self) 2253 self.SetOrientationMarker(mesh) 2254 2255 if utils.is_sequence(pos): 2256 self.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 2257 else: 2258 if pos < 2: 2259 self.SetViewport(0, 1 - 2 * size, size * 2, 1) 2260 elif pos == 2: 2261 self.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 2262 elif pos == 3: 2263 self.SetViewport(0, 0, size * 2, size * 2) 2264 elif pos == 4: 2265 self.SetViewport(1 - 2 * size, 0, 1, size * 2) 2266 2267 2268##################################################################### 2269def compute_visible_bounds(actors=None): 2270 """Calculate max meshes bounds and sizes.""" 2271 bns = [] 2272 2273 if actors is None: 2274 actors = vedo.plotter_instance.actors 2275 elif not utils.is_sequence(actors): 2276 actors = [actors] 2277 2278 try: 2279 # this block fails for VolumeSlice as vtkImageSlice.GetBounds() returns a pointer.. 2280 # in any case we dont need axes for that one. 2281 for a in actors: 2282 if a and a.GetUseBounds(): 2283 b = a.GetBounds() 2284 if b: 2285 bns.append(b) 2286 if bns: 2287 max_bns = np.max(bns, axis=0) 2288 min_bns = np.min(bns, axis=0) 2289 vbb = [min_bns[0], max_bns[1], min_bns[2], max_bns[3], min_bns[4], max_bns[5]] 2290 else: 2291 vbb = list(vedo.plotter_instance.renderer.ComputeVisiblePropBounds()) 2292 max_bns = vbb 2293 min_bns = vbb 2294 sizes = np.array( 2295 [max_bns[1] - min_bns[0], max_bns[3] - min_bns[2], max_bns[5] - min_bns[4]] 2296 ) 2297 return [vbb, sizes, min_bns, max_bns] 2298 except: 2299 return [[0, 0, 0, 0, 0, 0], [0, 0, 0], 0, 0] 2300 2301 2302##################################################################### 2303def Ruler( 2304 p1, 2305 p2, 2306 units_scale=1, 2307 label="", 2308 s=None, 2309 font=None, 2310 italic=0, 2311 prefix="", 2312 units="", # eg.'μm' 2313 c=(0.2, 0.1, 0.1), 2314 alpha=1, 2315 lw=1, 2316 precision=3, 2317 label_rotation=0, 2318 axis_rotation=0, 2319 tick_angle=90, 2320): 2321 """ 2322 Build a 3D ruler to indicate the distance of two points p1 and p2. 2323 2324 Arguments: 2325 label : (str) 2326 alternative fixed label to be shown 2327 units_scale : (float) 2328 factor to scale units (e.g. μm to mm) 2329 s : (float) 2330 size of the label 2331 font : (str) 2332 font face. Check [available fonts here](https://vedo.embl.es/fonts). 2333 italic : (float) 2334 italicness of the font in the range [0,1] 2335 units : (str) 2336 string to be appended to the numeric value 2337 lw : (int) 2338 line width in pixel units 2339 precision : (int) 2340 nr of significant digits to be shown 2341 label_rotation : (float) 2342 initial rotation of the label around the z-axis 2343 axis_rotation : (float) 2344 initial rotation of the line around the main axis 2345 tick_angle : (float) 2346 initial rotation of the line around the main axis 2347 2348 Examples: 2349 - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py) 2350 2351 ![](https://vedo.embl.es/images/pyplot/goniometer.png) 2352 """ 2353 if units_scale != 1.0 and units == "": 2354 raise ValueError( 2355 "When setting 'units_scale' to a value other than 1, " 2356 + "a 'units' arguments must be specified." 2357 ) 2358 2359 if isinstance(p1, Points): 2360 p1 = p1.GetPosition() 2361 if isinstance(p2, Points): 2362 p2 = p2.GetPosition() 2363 2364 if len(p1) == 2: 2365 p1 = [p1[0], p1[1], 0.0] 2366 if len(p2) == 2: 2367 p2 = [p2[0], p2[1], 0.0] 2368 2369 p1, p2 = np.array(p1), np.array(p2) 2370 q1, q2 = [0, 0, 0], [utils.mag(p2 - p1), 0, 0] 2371 q1, q2 = np.array(q1), np.array(q2) 2372 v = q2 - q1 2373 d = utils.mag(v) * units_scale 2374 2375 if s is None: 2376 s = d * 0.02 * (1 / units_scale) 2377 2378 if not label: 2379 label = str(d) 2380 if precision: 2381 label = utils.precision(d, precision) 2382 if prefix: 2383 label = prefix + "~" + label 2384 if units: 2385 label += "~" + units 2386 2387 lb = shapes.Text3D(label, pos=(q1 + q2) / 2, s=s, font=font, italic=italic, justify="center") 2388 if label_rotation: 2389 lb.RotateZ(label_rotation) 2390 2391 x0, x1 = lb.xbounds() 2392 gap = [(x1 - x0) / 2, 0, 0] 2393 pc1 = (v / 2 - gap) * 0.9 + q1 2394 pc2 = q2 - (v / 2 - gap) * 0.9 2395 2396 lc1 = shapes.Line(q1 - v / 50, pc1) 2397 lc2 = shapes.Line(q2 + v / 50, pc2) 2398 2399 zs = np.array([0, d / 50 * (1 / units_scale), 0]) 2400 ml1 = shapes.Line(-zs, zs).pos(q1) 2401 ml2 = shapes.Line(-zs, zs).pos(q2) 2402 ml1.RotateZ(tick_angle - 90) 2403 ml2.RotateZ(tick_angle - 90) 2404 2405 c1 = shapes.Circle(q1, r=d / 180 * (1 / units_scale), res=20) 2406 c2 = shapes.Circle(q2, r=d / 180 * (1 / units_scale), res=20) 2407 2408 acts = [lb, lc1, lc2, c1, c2, ml1, ml2] 2409 macts = merge(acts).pos(p1).c(c).alpha(alpha) 2410 macts.GetProperty().LightingOff() 2411 macts.GetProperty().SetLineWidth(lw) 2412 macts.UseBoundsOff() 2413 macts.base = q1 2414 macts.top = q2 2415 macts.orientation(p2 - p1, rotation=axis_rotation).bc("t").pickable(False) 2416 return macts 2417 2418 2419def RulerAxes( 2420 inputobj, 2421 xtitle="", 2422 ytitle="", 2423 ztitle="", 2424 xlabel="", 2425 ylabel="", 2426 zlabel="", 2427 xpadding=0.05, 2428 ypadding=0.04, 2429 zpadding=0, 2430 font="Normografo", 2431 s=None, 2432 italic=0, 2433 units="", 2434 c=(0.2, 0, 0), 2435 alpha=1, 2436 lw=1, 2437 precision=3, 2438 label_rotation=0, 2439 xaxis_rotation=0, 2440 yaxis_rotation=0, 2441 zaxis_rotation=0, 2442 xycross=True, 2443): 2444 """ 2445 A 3D ruler axes to indicate the sizes of the input scene or object. 2446 2447 Arguments: 2448 xtitle : (str) 2449 name of the axis or title 2450 xlabel : (str) 2451 alternative fixed label to be shown instead of the distance 2452 s : (float) 2453 size of the label 2454 font : (str) 2455 font face. Check [available fonts here](https://vedo.embl.es/fonts). 2456 italic : (float) 2457 italicness of the font in the range [0,1] 2458 units : (str) 2459 string to be appended to the numeric value 2460 lw : (int) 2461 line width in pixel units 2462 precision : (int) 2463 nr of significant digits to be shown 2464 label_rotation : (float) 2465 initial rotation of the label around the z-axis 2466 [x,y,z]axis_rotation : (float) 2467 initial rotation of the line around the main axis in degrees 2468 xycross : (bool) 2469 show two back crossing lines in the xy plane 2470 2471 Examples: 2472 - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py) 2473 """ 2474 if utils.is_sequence(inputobj): 2475 x0, x1, y0, y1, z0, z1 = inputobj 2476 else: 2477 x0, x1, y0, y1, z0, z1 = inputobj.bounds() 2478 dx, dy, dz = (y1 - y0) * xpadding, (x1 - x0) * ypadding, (y1 - y0) * zpadding 2479 d = np.sqrt((y1 - y0) ** 2 + (x1 - x0) ** 2 + (z1 - z0) ** 2) 2480 2481 if not d: 2482 return None 2483 2484 if s is None: 2485 s = d / 75 2486 2487 acts, rx, ry = [], None, None 2488 if xtitle is not None and (x1 - x0) / d > 0.1: 2489 rx = Ruler( 2490 [x0, y0 - dx, z0], 2491 [x1, y0 - dx, z0], 2492 s=s, 2493 font=font, 2494 precision=precision, 2495 label_rotation=label_rotation, 2496 axis_rotation=xaxis_rotation, 2497 lw=lw, 2498 italic=italic, 2499 prefix=xtitle, 2500 label=xlabel, 2501 units=units, 2502 ) 2503 acts.append(rx) 2504 if ytitle is not None and (y1 - y0) / d > 0.1: 2505 ry = Ruler( 2506 [x1 + dy, y0, z0], 2507 [x1 + dy, y1, z0], 2508 s=s, 2509 font=font, 2510 precision=precision, 2511 label_rotation=label_rotation, 2512 axis_rotation=yaxis_rotation, 2513 lw=lw, 2514 italic=italic, 2515 prefix=ytitle, 2516 label=ylabel, 2517 units=units, 2518 ) 2519 acts.append(ry) 2520 if ztitle is not None and (z1 - z0) / d > 0.1: 2521 rz = Ruler( 2522 [x0 - dy, y0 + dz, z0], 2523 [x0 - dy, y0 + dz, z1], 2524 s=s, 2525 font=font, 2526 precision=precision, 2527 label_rotation=label_rotation, 2528 axis_rotation=zaxis_rotation + 90, 2529 lw=lw, 2530 italic=italic, 2531 prefix=ztitle, 2532 label=zlabel, 2533 units=units, 2534 ) 2535 acts.append(rz) 2536 2537 if xycross and rx and ry: 2538 lx = shapes.Line([x0, y0, z0], [x0, y1 + dx, z0]) 2539 ly = shapes.Line([x0 - dy, y1, z0], [x1, y1, z0]) 2540 d = min((x1 - x0), (y1 - y0)) / 200 2541 cxy = shapes.Circle([x0, y1, z0], r=d, res=15) 2542 acts.extend([lx, ly, cxy]) 2543 2544 macts = merge(acts) 2545 if not macts: 2546 return None 2547 macts.c(c).alpha(alpha).bc("t") 2548 macts.UseBoundsOff() 2549 macts.PickableOff() 2550 return macts 2551 2552 2553##################################################################### 2554class Ruler2D(vtk.vtkAxisActor2D): 2555 """ 2556 Create a ruler with tick marks, labels and a title. 2557 """ 2558 2559 def __init__( 2560 self, 2561 lw=2, 2562 ticks=True, 2563 labels=False, 2564 c="k", 2565 alpha=1, 2566 title="", 2567 font="Calco", 2568 font_size=24, 2569 bc=None, 2570 ): 2571 """ 2572 Create a ruler with tick marks, labels and a title. 2573 2574 Ruler2D is a 2D actor; that is, it is drawn on the overlay 2575 plane and is not occluded by 3D geometry. 2576 To use this class, specify two points defining the start and end 2577 with update_points() as 3D points. 2578 2579 This class decides decides how to create reasonable tick 2580 marks and labels. 2581 2582 Labels are drawn on the "right" side of the axis. 2583 The "right" side is the side of the axis on the right. 2584 The way the labels and title line up with the axis and tick marks 2585 depends on whether the line is considered horizontal or vertical. 2586 2587 Arguments: 2588 lw : (int) 2589 width of the line in pixel units 2590 ticks : (bool) 2591 control if drawing the tick marks 2592 labels : (bool) 2593 control if drawing the numeric labels 2594 c : (color) 2595 color of the object 2596 alpha : (float) 2597 opacity of the object 2598 title : (str) 2599 title of the ruler 2600 font : (str) 2601 font face name. Check [available fonts here](https://vedo.embl.es/fonts). 2602 font_size : (int) 2603 font size 2604 bc : (color) 2605 background color of the title 2606 2607 Example: 2608 ```python 2609 from vedo import * 2610 plt = Plotter(axes=1, interactive=False) 2611 plt.show(Cube()) 2612 rul = Ruler2D() 2613 rul.set_points([0,0,0], [0.5,0.5,0.5]) 2614 plt.add(rul) 2615 plt.interactive().close() 2616 ``` 2617 ![](https://vedo.embl.es/images/feats/dist_tool.png) 2618 """ 2619 vtk.vtkAxisActor2D.__init__(self) 2620 2621 plt = vedo.plotter_instance 2622 if not plt: 2623 vedo.logger.error("Ruler2D need to initialize Plotter first.") 2624 raise RuntimeError() 2625 2626 self.p0 = [0, 0, 0] 2627 self.p1 = [0, 0, 0] 2628 self.distance = 0 2629 self.title = title 2630 2631 prop = self.GetProperty() 2632 tprop = self.GetTitleTextProperty() 2633 2634 self.SetTitle(title) 2635 self.SetNumberOfLabels(9) 2636 2637 if not font: 2638 font = settings.default_font 2639 if font.lower() == "courier": 2640 tprop.SetFontFamilyToCourier() 2641 elif font.lower() == "times": 2642 tprop.SetFontFamilyToTimes() 2643 elif font.lower() == "arial": 2644 tprop.SetFontFamilyToArial() 2645 else: 2646 tprop.SetFontFamily(vtk.VTK_FONT_FILE) 2647 tprop.SetFontFile(utils.get_font_path(font)) 2648 tprop.SetFontSize(font_size) 2649 tprop.BoldOff() 2650 tprop.ItalicOff() 2651 tprop.ShadowOff() 2652 tprop.SetColor(get_color(c)) 2653 tprop.SetOpacity(alpha) 2654 if bc is not None: 2655 bc = get_color(bc) 2656 tprop.SetBackgroundColor(bc) 2657 tprop.SetBackgroundOpacity(alpha) 2658 2659 lprop = vtk.vtkTextProperty() 2660 lprop.ShallowCopy(tprop) 2661 self.SetLabelTextProperty(lprop) 2662 2663 self.SetLabelFormat("%0.3g") 2664 self.SetTickVisibility(ticks) 2665 self.SetLabelVisibility(labels) 2666 prop.SetLineWidth(lw) 2667 prop.SetColor(get_color(c)) 2668 2669 self.renderer = plt.renderer 2670 self.cid = plt.interactor.AddObserver("RenderEvent", self._update_viz, 1.0) 2671 2672 def color(self, c): 2673 """Assign a new color.""" 2674 c = get_color(c) 2675 self.GetTitleTextProperty().SetColor(c) 2676 self.GetLabelTextProperty().SetColor(c) 2677 self.GetProperty().SetColor(c) 2678 return self 2679 2680 def off(self): 2681 """Switch off the ruler completely.""" 2682 self.renderer.RemoveObserver(self.cid) 2683 self.renderer.RemoveActor(self) 2684 2685 def set_points(self, p0, p1): 2686 """Set new values for the ruler start and end points.""" 2687 self.p0 = np.asarray(p0) 2688 self.p1 = np.asarray(p1) 2689 self._update_viz(0, 0) 2690 return self 2691 2692 def _update_viz(self, evt, name): 2693 ren = self.renderer 2694 view_size = np.array(ren.GetSize()) 2695 2696 ren.SetWorldPoint(*self.p0, 1) 2697 ren.WorldToDisplay() 2698 disp_point1 = ren.GetDisplayPoint()[:2] 2699 disp_point1 = np.array(disp_point1) / view_size 2700 2701 ren.SetWorldPoint(*self.p1, 1) 2702 ren.WorldToDisplay() 2703 disp_point2 = ren.GetDisplayPoint()[:2] 2704 disp_point2 = np.array(disp_point2) / view_size 2705 2706 self.SetPoint1(*disp_point1) 2707 self.SetPoint2(*disp_point2) 2708 self.distance = np.linalg.norm(self.p1 - self.p0) 2709 self.SetRange(0, self.distance) 2710 if not self.title: 2711 self.SetTitle(utils.precision(self.distance, 3)) 2712 2713 2714##################################################################### 2715class DistanceTool(Group): 2716 """ 2717 Create a tool to measure the distance between two clicked points. 2718 """ 2719 2720 def __init__(self, plotter=None, c="k", lw=2): 2721 """ 2722 Create a tool to measure the distance between two clicked points. 2723 2724 Example: 2725 ```python 2726 from vedo import * 2727 mesh = ParametricShape("RandomHills").c("red5") 2728 plt = Plotter(axes=1) 2729 dtool = DistanceTool() 2730 dtool.on() 2731 plt.show(mesh, dtool) 2732 dtool.off() 2733 ``` 2734 ![](https://vedo.embl.es/images/feats/dist_tool.png) 2735 """ 2736 Group.__init__(self) 2737 2738 self.p0 = [0, 0, 0] 2739 self.p1 = [0, 0, 0] 2740 self.distance = 0 2741 if plotter is None: 2742 plotter = vedo.plotter_instance 2743 self.plotter = plotter 2744 self.callback = None 2745 self.cid = None 2746 self.color = c 2747 self.linewidth = lw 2748 self.toggle = True 2749 self.ruler = None 2750 self.title = "" 2751 2752 def on(self): 2753 """Switch tool on.""" 2754 self.cid = self.plotter.add_callback("click", self._onclick) 2755 self.VisibilityOn() 2756 self.plotter.render() 2757 return self 2758 2759 def off(self): 2760 """Switch tool off.""" 2761 self.plotter.remove_callback(self.cid) 2762 self.VisibilityOff() 2763 self.ruler.off() 2764 self.plotter.render() 2765 2766 def _onclick(self, event): 2767 if not event.actor: 2768 return 2769 2770 self.clear() 2771 2772 acts = [] 2773 if self.toggle: 2774 self.p0 = event.picked3d 2775 acts.append(Point(self.p0, c=self.color)) 2776 else: 2777 self.p1 = event.picked3d 2778 self.distance = np.linalg.norm(self.p1 - self.p0) 2779 acts.append(Point(self.p0, c=self.color)) 2780 acts.append(Point(self.p1, c=self.color)) 2781 self.ruler = Ruler2D(c=self.color) 2782 self.ruler.set_points(self.p0, self.p1) 2783 acts.append(self.ruler) 2784 2785 if self.callback is not None: 2786 self.callback(event) 2787 2788 self += acts 2789 self.toggle = not self.toggle 2790 2791 2792##################################################################### 2793def Axes( 2794 obj=None, 2795 xtitle='x', ytitle='y', ztitle='z', 2796 xrange=None, yrange=None, zrange=None, 2797 c=None, 2798 number_of_divisions=None, 2799 digits=None, 2800 limit_ratio=0.04, 2801 title_depth=0, 2802 title_font="", # grab settings.default_font 2803 text_scale=1.0, 2804 x_values_and_labels=None, y_values_and_labels=None, z_values_and_labels=None, 2805 htitle="", 2806 htitle_size=0.03, 2807 htitle_font=None, 2808 htitle_italic=False, 2809 htitle_color=None, 2810 htitle_justify='bottom-center', 2811 htitle_rotation=0, 2812 htitle_offset=(0, 0.01, 0), 2813 xtitle_position=0.95, ytitle_position=0.95, ztitle_position=0.95, 2814 xtitle_offset=0.025, ytitle_offset=0.0275, ztitle_offset=0.02, # can be a list (dx,dy,dz) 2815 xtitle_justify=None, ytitle_justify=None, ztitle_justify=None, 2816 xtitle_rotation=0, ytitle_rotation=0, ztitle_rotation=0, # can be a list (rx,ry,rz) 2817 xtitle_box=False, ytitle_box=False, 2818 xtitle_size=0.025, ytitle_size=0.025, ztitle_size=0.025, 2819 xtitle_color=None, ytitle_color=None, ztitle_color=None, 2820 xtitle_backface_color=None, ytitle_backface_color=None, ztitle_backface_color=None, 2821 xtitle_italic=0, ytitle_italic=0, ztitle_italic=0, 2822 grid_linewidth=1, 2823 xygrid=True, yzgrid=False, zxgrid=False, 2824 xygrid2=False, yzgrid2=False, zxgrid2=False, 2825 xygrid_transparent=False, yzgrid_transparent=False, zxgrid_transparent=False, 2826 xygrid2_transparent=False, yzgrid2_transparent=False, zxgrid2_transparent=False, 2827 xyplane_color=None, yzplane_color=None, zxplane_color=None, 2828 xygrid_color=None, yzgrid_color=None, zxgrid_color=None, 2829 xyalpha=0.075, yzalpha=0.075, zxalpha=0.075, 2830 xyframe_line=None, yzframe_line=None, zxframe_line=None, 2831 xyframe_color=None, yzframe_color=None, zxframe_color=None, 2832 axes_linewidth=1, 2833 xline_color=None, yline_color=None, zline_color=None, 2834 xhighlight_zero=False, yhighlight_zero=False, zhighlight_zero=False, 2835 xhighlight_zero_color='red4', yhighlight_zero_color='green4', zhighlight_zero_color='blue4', 2836 show_ticks=True, 2837 xtick_length=0.015, ytick_length=0.015, ztick_length=0.015, 2838 xtick_thickness=0.0025, ytick_thickness=0.0025, ztick_thickness=0.0025, 2839 xminor_ticks=1, yminor_ticks=1, zminor_ticks=1, 2840 tip_size=None, 2841 label_font="", # grab settings.default_font 2842 xlabel_color=None, ylabel_color=None, zlabel_color=None, 2843 xlabel_size=0.016, ylabel_size=0.016, zlabel_size=0.016, 2844 xlabel_offset=0.8, ylabel_offset=0.8, zlabel_offset=0.8, # each can be a list (dx,dy,dz) 2845 xlabel_justify=None, ylabel_justify=None, zlabel_justify=None, 2846 xlabel_rotation=0, ylabel_rotation=0, zlabel_rotation=0, # each can be a list (rx,ry,rz) 2847 xaxis_rotation=0, yaxis_rotation=0, zaxis_rotation=0, # rotate all elements around axis 2848 xyshift=0, yzshift=0, zxshift=0, 2849 xshift_along_y=0, xshift_along_z=0, 2850 yshift_along_x=0, yshift_along_z=0, 2851 zshift_along_x=0, zshift_along_y=0, 2852 x_use_bounds=True, y_use_bounds=True, z_use_bounds=False, 2853 x_inverted=False, y_inverted=False, z_inverted=False, 2854 use_global=False, 2855 tol=0.001, 2856 **options, 2857 ): 2858 """ 2859 Draw axes for the input object. 2860 Check [available fonts here](https://vedo.embl.es/fonts). 2861 2862 Returns an `vedo.Assembly` object. 2863 2864 Parameters 2865 ---------- 2866 2867 - `xtitle`, ['x'], x-axis title text 2868 - `xrange`, [None], x-axis range in format (xmin, ymin), default is automatic. 2869 - `number_of_divisions`, [None], approximate number of divisions on the longest axis 2870 - `axes_linewidth`, [1], width of the axes lines 2871 - `grid_linewidth`, [1], width of the grid lines 2872 - `title_depth`, [0], extrusion fractional depth of title text 2873 - `x_values_and_labels` [], assign custom tick positions and labels [(pos1, label1), ...] 2874 - `xygrid`, [True], show a gridded wall on plane xy 2875 - `yzgrid`, [True], show a gridded wall on plane yz 2876 - `zxgrid`, [True], show a gridded wall on plane zx 2877 - `zxgrid2`, [False], show zx plane on opposite side of the bounding box 2878 - `xygrid_transparent` [False], make grid plane completely transparent 2879 - `xygrid2_transparent` [False], make grid plane completely transparent on opposite side box 2880 - `xyplane_color`, ['None'], color of the plane 2881 - `xygrid_color`, ['None'], grid line color 2882 - `xyalpha`, [0.15], grid plane opacity 2883 - `xyframe_line`, [0], add a frame for the plane, use value as the thickness 2884 - `xyframe_color`, [None], color for the frame of the plane 2885 - `show_ticks`, [True], show major ticks 2886 - `digits`, [None], use this number of significant digits in scientific notation 2887 - `title_font`, [''], font for axes titles 2888 - `label_font`, [''], font for numeric labels 2889 - `text_scale`, [1.0], global scaling factor for all text elements (titles, labels) 2890 - `htitle`, [''], header title 2891 - `htitle_size`, [0.03], header title size 2892 - `htitle_font`, [None], header font (defaults to `title_font`) 2893 - `htitle_italic`, [True], header font is italic 2894 - `htitle_color`, [None], header title color (defaults to `xtitle_color`) 2895 - `htitle_justify`, ['bottom-center'], origin of the title justification 2896 - `htitle_offset`, [(0,0.01,0)], control offsets of header title in x, y and z 2897 - `xtitle_position`, [0.32], title fractional positions along axis 2898 - `xtitle_offset`, [0.05], title fractional offset distance from axis line, can be a list 2899 - `xtitle_justify`, [None], choose the origin of the bounding box of title 2900 - `xtitle_rotation`, [0], add a rotation of the axis title, can be a list (rx,ry,rz) 2901 - `xtitle_box`, [False], add a box around title text 2902 - `xline_color`, [automatic], color of the x-axis 2903 - `xtitle_color`, [automatic], color of the axis title 2904 - `xtitle_backface_color`, [None], color of axis title on its backface 2905 - `xtitle_size`, [0.025], size of the axis title 2906 - `xtitle_italic`, [0], a bool or float to make the font italic 2907 - `xhighlight_zero`, [True], draw a line highlighting zero position if in range 2908 - `xhighlight_zero_color`, [autom], color of the line highlighting the zero position 2909 - `xtick_length`, [0.005], radius of the major ticks 2910 - `xtick_thickness`, [0.0025], thickness of the major ticks along their axis 2911 - `xminor_ticks`, [1], number of minor ticks between two major ticks 2912 - `xlabel_color`, [automatic], color of numeric labels and ticks 2913 - `xLabelPrecision`, [2], nr. of significative digits to be shown 2914 - `xlabel_size`, [0.015], size of the numeric labels along axis 2915 - `xlabel_rotation`, [0], numeric labels rotation (can be a list of 3 rotations) 2916 - `xlabel_offset`, [0.8], offset of the numeric labels (can be a list of 3 offsets) 2917 - `xlabel_justify`, [None], choose the origin of the bounding box of labels 2918 - `xaxis_rotation`, [0], rotate the X axis elements (ticks and labels) around this same axis 2919 - `xyshift` [0.0], slide the xy-plane along z (the range is [0,1]) 2920 - `xshift_along_y` [0.0], slide x-axis along the y-axis (the range is [0,1]) 2921 - `tip_size`, [0.01], size of the arrow tip 2922 - `limit_ratio`, [0.04], below this ratio don't plot smaller axis 2923 - `x_use_bounds`, [True], keep into account space occupied by labels when setting camera 2924 - `x_inverted`, [False], invert labels order and direction (only visually!) 2925 - `use_global`, [False], try to compute the global bounding box of visible actors 2926 2927 Example: 2928 ```python 2929 from vedo import Axes, Box, show 2930 box = Box(pos=(1,2,3), length=8, width=9, height=7).alpha(0.1) 2931 axs = Axes(box, c='k') # returns an Assembly object 2932 for a in axs.unpack(): 2933 print(a.name) 2934 show(box, axs).close() 2935 ``` 2936 ![](https://vedo.embl.es/images/feaxes1ats/axes1.png) 2937 2938 Examples: 2939 - [custom_axes1.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes1.py) 2940 - [custom_axes2.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes2.py) 2941 - [custom_axes3.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes3.py) 2942 - [custom_axes4.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes4.py) 2943 2944 ![](https://vedo.embl.es/images/pyplot/customAxes3.png) 2945 """ 2946 # make sure options are correct and user passed snake_case instead of camelCase 2947 if len(options): 2948 for k in options: 2949 if k.lower() == k: 2950 vedo.logger.warning(f"Unrecognised keyword '{k}' is ignored") 2951 else: 2952 vedo.logger.warning(f"Unrecognised keyword '{k}'. Please use snake_case notation") 2953 2954 if not title_font: 2955 title_font = settings.default_font 2956 if not label_font: 2957 label_font = settings.default_font 2958 2959 if c is None: # automatic black or white 2960 c = (0.1, 0.1, 0.1) 2961 plt = vedo.plotter_instance 2962 if plt and plt.renderer: 2963 bgcol = plt.renderer.GetBackground() 2964 else: 2965 bgcol = (1, 1, 1) 2966 if np.sum(bgcol) < 1.5: 2967 c = (0.9, 0.9, 0.9) 2968 else: 2969 c = get_color(c) 2970 2971 if use_global: 2972 vbb, drange, min_bns, max_bns = compute_visible_bounds() 2973 else: 2974 if obj is not None: 2975 vbb, drange, min_bns, max_bns = compute_visible_bounds(obj) 2976 else: 2977 vbb = np.zeros(6) 2978 drange = np.zeros(3) 2979 if zrange is None: 2980 zrange = (0, 0) 2981 if xrange is None or yrange is None: 2982 vedo.logger.error("in Axes() must specify axes ranges!") 2983 raise RuntimeError() 2984 2985 if xrange is not None: 2986 if xrange[1] < xrange[0]: 2987 x_inverted = True 2988 xrange = [xrange[1], xrange[0]] 2989 vbb[0], vbb[1] = xrange 2990 drange[0] = vbb[1] - vbb[0] 2991 min_bns = vbb 2992 max_bns = vbb 2993 if yrange is not None: 2994 if yrange[1] < yrange[0]: 2995 y_inverted = True 2996 yrange = [yrange[1], yrange[0]] 2997 vbb[2], vbb[3] = yrange 2998 drange[1] = vbb[3] - vbb[2] 2999 min_bns = vbb 3000 max_bns = vbb 3001 if zrange is not None: 3002 if zrange[1] < zrange[0]: 3003 z_inverted = True 3004 zrange = [zrange[1], zrange[0]] 3005 vbb[4], vbb[5] = zrange 3006 drange[2] = vbb[5] - vbb[4] 3007 min_bns = vbb 3008 max_bns = vbb 3009 3010 drangemax = max(drange) 3011 if not drangemax: 3012 return None 3013 3014 if drange[0] / drangemax < limit_ratio: 3015 drange[0] = 0 3016 xtitle = "" 3017 if drange[1] / drangemax < limit_ratio: 3018 drange[1] = 0 3019 ytitle = "" 3020 if drange[2] / drangemax < limit_ratio: 3021 drange[2] = 0 3022 ztitle = "" 3023 3024 x0, x1, y0, y1, z0, z1 = vbb 3025 dx, dy, dz = drange 3026 3027 gscale = np.sqrt(dx * dx + dy * dy + dz * dz) * 0.75 3028 3029 if not xyplane_color: xyplane_color = c 3030 if not yzplane_color: yzplane_color = c 3031 if not zxplane_color: zxplane_color = c 3032 if not xygrid_color: xygrid_color = c 3033 if not yzgrid_color: yzgrid_color = c 3034 if not zxgrid_color: zxgrid_color = c 3035 if not xtitle_color: xtitle_color = c 3036 if not ytitle_color: ytitle_color = c 3037 if not ztitle_color: ztitle_color = c 3038 if not xline_color: xline_color = c 3039 if not yline_color: yline_color = c 3040 if not zline_color: zline_color = c 3041 if not xlabel_color: xlabel_color = xline_color 3042 if not ylabel_color: ylabel_color = yline_color 3043 if not zlabel_color: zlabel_color = zline_color 3044 3045 # vtk version<9 dont like depthpeeling: force switching off grids 3046 if settings.use_depth_peeling and not utils.vtk_version_at_least(9): 3047 xygrid = False 3048 yzgrid = False 3049 zxgrid = False 3050 xygrid2 = False 3051 yzgrid2 = False 3052 zxgrid2 = False 3053 3054 if tip_size is None: 3055 tip_size = 0.005 * gscale 3056 if not ztitle: 3057 tip_size = 0 # switch off in xy 2d 3058 3059 ndiv = 4 3060 if not ztitle or not ytitle or not xtitle: # make more default ticks if 2D 3061 ndiv = 6 3062 if not ztitle: 3063 if xyframe_line is None: 3064 xyframe_line = True 3065 if tip_size is None: 3066 tip_size = False 3067 3068 if utils.is_sequence(number_of_divisions): 3069 rx, ry, rz = number_of_divisions 3070 else: 3071 if not number_of_divisions: 3072 number_of_divisions = ndiv 3073 3074 rx, ry, rz = np.ceil(drange / drangemax * number_of_divisions).astype(int) 3075 3076 if xtitle: 3077 xticks_float, xticks_str = utils.make_ticks(x0, x1, rx, x_values_and_labels, digits) 3078 xticks_float = xticks_float * dx 3079 if x_inverted: 3080 xticks_float = np.flip(-(xticks_float - xticks_float[-1])) 3081 xticks_str = list(reversed(xticks_str)) 3082 xticks_str[-1] = "" 3083 xhighlight_zero = False 3084 if ytitle: 3085 yticks_float, yticks_str = utils.make_ticks(y0, y1, ry, y_values_and_labels, digits) 3086 yticks_float = yticks_float * dy 3087 if y_inverted: 3088 yticks_float = np.flip(-(yticks_float - yticks_float[-1])) 3089 yticks_str = list(reversed(yticks_str)) 3090 yticks_str[-1] = "" 3091 yhighlight_zero = False 3092 if ztitle: 3093 zticks_float, zticks_str = utils.make_ticks(z0, z1, rz, z_values_and_labels, digits) 3094 zticks_float = zticks_float * dz 3095 if z_inverted: 3096 zticks_float = np.flip(-(zticks_float - zticks_float[-1])) 3097 zticks_str = list(reversed(zticks_str)) 3098 zticks_str[-1] = "" 3099 zhighlight_zero = False 3100 3101 ################################################ axes lines 3102 lines = [] 3103 if xtitle: 3104 axlinex = shapes.Line([0,0,0], [dx,0,0], c=xline_color, lw=axes_linewidth) 3105 if xyshift: axlinex.shift(0,0,xyshift*dz) 3106 if zxshift: axlinex.shift(0,zxshift*dy,0) 3107 if xshift_along_y: axlinex.shift(0,xshift_along_y*dy,0) 3108 if xshift_along_z: axlinex.shift(0,0,xshift_along_z*dz) 3109 axlinex.name = 'xAxis' 3110 lines.append(axlinex) 3111 if ytitle: 3112 axliney = shapes.Line([0,0,0], [0,dy,0], c=yline_color, lw=axes_linewidth) 3113 if xyshift: axliney.shift(0,0,xyshift*dz) 3114 if yzshift: axliney.shift(yzshift*dx,0,0) 3115 if yshift_along_x: axliney.shift(yshift_along_x*dx,0,0) 3116 if yshift_along_z: axliney.shift(0,0,yshift_along_z*dz) 3117 axliney.name = 'yAxis' 3118 lines.append(axliney) 3119 if ztitle: 3120 axlinez = shapes.Line([0,0,0], [0,0,dz], c=zline_color, lw=axes_linewidth) 3121 if yzshift: axlinez.shift(yzshift*dx,0,0) 3122 if zxshift: axlinez.shift(0,zxshift*dy,0) 3123 if zshift_along_x: axlinez.shift(zshift_along_x*dx,0,0) 3124 if zshift_along_y: axlinez.shift(0,zshift_along_y*dy,0) 3125 axlinez.name = 'zAxis' 3126 lines.append(axlinez) 3127 3128 ################################################ grid planes 3129 # all shapes have a name to keep track of them in the Assembly 3130 # if user wants to unpack it 3131 grids = [] 3132 if xygrid and xtitle and ytitle: 3133 if not xygrid_transparent: 3134 gxy = shapes.Grid(s=(xticks_float, yticks_float)) 3135 gxy.alpha(xyalpha).c(xyplane_color).lw(0) 3136 if xyshift: gxy.shift(0,0,xyshift*dz) 3137 elif tol: gxy.shift(0,0,-tol*gscale) 3138 gxy.name = "xyGrid" 3139 grids.append(gxy) 3140 if grid_linewidth: 3141 gxy_lines = shapes.Grid(s=(xticks_float, yticks_float)) 3142 gxy_lines.c(xyplane_color).lw(grid_linewidth).alpha(xyalpha) 3143 if xyshift: gxy_lines.shift(0,0,xyshift*dz) 3144 elif tol: gxy_lines.shift(0,0,-tol*gscale) 3145 gxy_lines.name = "xyGridLines" 3146 grids.append(gxy_lines) 3147 3148 if yzgrid and ytitle and ztitle: 3149 if not yzgrid_transparent: 3150 gyz = shapes.Grid(s=(zticks_float, yticks_float)) 3151 gyz.alpha(yzalpha).c(yzplane_color).lw(0).RotateY(-90) 3152 if yzshift: gyz.shift(yzshift*dx,0,0) 3153 elif tol: gyz.shift(-tol*gscale,0,0) 3154 gyz.name = "yzGrid" 3155 grids.append(gyz) 3156 if grid_linewidth: 3157 gyz_lines = shapes.Grid(s=(zticks_float, yticks_float)) 3158 gyz_lines.c(yzplane_color).lw(grid_linewidth).alpha(yzalpha).RotateY(-90) 3159 if yzshift: gyz_lines.shift(yzshift*dx,0,0) 3160 elif tol: gyz_lines.shift(-tol*gscale,0,0) 3161 gyz_lines.name = "yzGridLines" 3162 grids.append(gyz_lines) 3163 3164 if zxgrid and ztitle and xtitle: 3165 if not zxgrid_transparent: 3166 gzx = shapes.Grid(s=(xticks_float, zticks_float)) 3167 gzx.alpha(zxalpha).c(zxplane_color).lw(0).RotateX(90) 3168 if zxshift: gzx.shift(0,zxshift*dy,0) 3169 elif tol: gzx.shift(0,-tol*gscale,0) 3170 gzx.name = "zxGrid" 3171 grids.append(gzx) 3172 if grid_linewidth: 3173 gzx_lines = shapes.Grid(s=(xticks_float, zticks_float)) 3174 gzx_lines.c(zxplane_color).lw(grid_linewidth).alpha(zxalpha).RotateX(90) 3175 if zxshift: gzx_lines.shift(0,zxshift*dy,0) 3176 elif tol: gzx_lines.shift(0,-tol*gscale,0) 3177 gzx_lines.name = "zxGridLines" 3178 grids.append(gzx_lines) 3179 3180 # Grid2 3181 if xygrid2 and xtitle and ytitle: 3182 if not xygrid2_transparent: 3183 gxy2 = shapes.Grid(s=(xticks_float, yticks_float)).z(dz) 3184 gxy2.alpha(xyalpha).c(xyplane_color).lw(0) 3185 if tol: gxy2.shift(0,tol*gscale,0) 3186 gxy2.name = "xyGrid2" 3187 grids.append(gxy2) 3188 if grid_linewidth: 3189 gxy2_lines = shapes.Grid(s=(xticks_float, yticks_float)).z(dz) 3190 gxy2_lines.c(xyplane_color).lw(grid_linewidth).alpha(xyalpha) 3191 if tol: gxy2_lines.shift(0,tol*gscale,0) 3192 gxy2_lines.name = "xygrid2Lines" 3193 grids.append(gxy2_lines) 3194 3195 if yzgrid2 and ytitle and ztitle: 3196 if not yzgrid2_transparent: 3197 gyz2 = shapes.Grid(s=(zticks_float, yticks_float)).x(dx) 3198 gyz2.alpha(yzalpha).c(yzplane_color).lw(0).RotateY(-90) 3199 if tol: gyz2.shift(tol*gscale,0,0) 3200 gyz2.name = "yzGrid2" 3201 grids.append(gyz2) 3202 if grid_linewidth: 3203 gyz2_lines = shapes.Grid(s=(zticks_float, yticks_float)).x(dx) 3204 gyz2_lines.c(yzplane_color).lw(grid_linewidth).alpha(yzalpha).RotateY(-90) 3205 if tol: gyz2_lines.shift(tol*gscale,0,0) 3206 gyz2_lines.name = "yzGrid2Lines" 3207 grids.append(gyz2_lines) 3208 3209 if zxgrid2 and ztitle and xtitle: 3210 if not zxgrid2_transparent: 3211 gzx2 = shapes.Grid(s=(xticks_float, zticks_float)).y(dy) 3212 gzx2.alpha(zxalpha).c(zxplane_color).lw(0).RotateX(90) 3213 if tol: gzx2.shift(0,tol*gscale,0) 3214 gzx2.name = "zxGrid2" 3215 grids.append(gzx2) 3216 if grid_linewidth: 3217 gzx2_lines = shapes.Grid(s=(xticks_float, zticks_float)).y(dy) 3218 gzx2_lines.c(zxplane_color).lw(grid_linewidth).alpha(zxalpha).RotateX(90) 3219 if tol: gzx2_lines.shift(0,tol*gscale,0) 3220 gzx2_lines.name = "zxGrid2Lines" 3221 grids.append(gzx2_lines) 3222 3223 ################################################ frame lines 3224 framelines = [] 3225 if xyframe_line and xtitle and ytitle: 3226 if not xyframe_color: 3227 xyframe_color = xygrid_color 3228 frxy = shapes.Line([[0,dy,0],[dx,dy,0],[dx,0,0],[0,0,0],[0,dy,0]], 3229 c=xyframe_color, lw=xyframe_line) 3230 if xyshift: frxy.shift(0,0,xyshift*dz) 3231 frxy.name = 'xyFrameLine' 3232 framelines.append(frxy) 3233 if yzframe_line and ytitle and ztitle: 3234 if not yzframe_color: 3235 yzframe_color = yzgrid_color 3236 fryz = shapes.Line([[0,0,dz],[0,dy,dz],[0,dy,0],[0,0,0],[0,0,dz]], 3237 c=yzframe_color, lw=yzframe_line) 3238 if yzshift: fryz.shift(yzshift*dx,0,0) 3239 fryz.name = 'yzFrameLine' 3240 framelines.append(fryz) 3241 if zxframe_line and ztitle and xtitle: 3242 if not zxframe_color: 3243 zxframe_color = zxgrid_color 3244 frzx = shapes.Line([[0,0,dz],[dx,0,dz],[dx,0,0],[0,0,0],[0,0,dz]], 3245 c=zxframe_color, lw=zxframe_line) 3246 if zxshift: frzx.shift(0,zxshift*dy,0) 3247 frzx.name = 'zxFrameLine' 3248 framelines.append(frzx) 3249 3250 ################################################ zero lines highlights 3251 highlights = [] 3252 if xygrid and xtitle and ytitle: 3253 if xhighlight_zero and min_bns[0] <= 0 and max_bns[1] > 0: 3254 xhl = -min_bns[0] 3255 hxy = shapes.Line([xhl,0,0], [xhl,dy,0], c=xhighlight_zero_color) 3256 hxy.alpha(np.sqrt(xyalpha)).lw(grid_linewidth*2) 3257 if xyshift: hxy.shift(0,0,xyshift*dz) 3258 hxy.name = "xyHighlightZero" 3259 highlights.append(hxy) 3260 if yhighlight_zero and min_bns[2] <= 0 and max_bns[3] > 0: 3261 yhl = -min_bns[2] 3262 hyx = shapes.Line([0,yhl,0], [dx,yhl,0], c=yhighlight_zero_color) 3263 hyx.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2) 3264 if xyshift: hyx.shift(0,0,xyshift*dz) 3265 hyx.name = "yxHighlightZero" 3266 highlights.append(hyx) 3267 3268 if yzgrid and ytitle and ztitle: 3269 if yhighlight_zero and min_bns[2] <= 0 and max_bns[3] > 0: 3270 yhl = -min_bns[2] 3271 hyz = shapes.Line([0,yhl,0], [0,yhl,dz], c=yhighlight_zero_color) 3272 hyz.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2) 3273 if yzshift: hyz.shift(yzshift*dx,0,0) 3274 hyz.name = "yzHighlightZero" 3275 highlights.append(hyz) 3276 if zhighlight_zero and min_bns[4] <= 0 and max_bns[5] > 0: 3277 zhl = -min_bns[4] 3278 hzy = shapes.Line([0,0,zhl], [0,dy,zhl], c=zhighlight_zero_color) 3279 hzy.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2) 3280 if yzshift: hzy.shift(yzshift*dx,0,0) 3281 hzy.name = "zyHighlightZero" 3282 highlights.append(hzy) 3283 3284 if zxgrid and ztitle and xtitle: 3285 if zhighlight_zero and min_bns[4] <= 0 and max_bns[5] > 0: 3286 zhl = -min_bns[4] 3287 hzx = shapes.Line([0,0,zhl], [dx,0,zhl], c=zhighlight_zero_color) 3288 hzx.alpha(np.sqrt(zxalpha)).lw(grid_linewidth*2) 3289 if zxshift: hzx.shift(0,zxshift*dy,0) 3290 hzx.name = "zxHighlightZero" 3291 highlights.append(hzx) 3292 if xhighlight_zero and min_bns[0] <= 0 and max_bns[1] > 0: 3293 xhl = -min_bns[0] 3294 hxz = shapes.Line([xhl,0,0], [xhl,0,dz], c=xhighlight_zero_color) 3295 hxz.alpha(np.sqrt(zxalpha)).lw(grid_linewidth*2) 3296 if zxshift: hxz.shift(0,zxshift*dy,0) 3297 hxz.name = "xzHighlightZero" 3298 highlights.append(hxz) 3299 3300 ################################################ arrow cone 3301 cones = [] 3302 3303 if tip_size: 3304 3305 if xtitle: 3306 if x_inverted: 3307 cx = shapes.Cone( 3308 r=tip_size, height=tip_size * 2, axis=(-1, 0, 0), c=xline_color, res=12 3309 ) 3310 else: 3311 cx = shapes.Cone((dx,0,0), r=tip_size, height=tip_size*2, 3312 axis=(1,0,0), c=xline_color, res=12) 3313 if xyshift: cx.shift(0,0,xyshift*dz) 3314 if zxshift: cx.shift(0,zxshift*dy,0) 3315 if xshift_along_y: cx.shift(0,xshift_along_y*dy,0) 3316 if xshift_along_z: cx.shift(0,0,xshift_along_z*dz) 3317 cx.name = "xTipCone" 3318 cones.append(cx) 3319 3320 if ytitle: 3321 if y_inverted: 3322 cy = shapes.Cone(r=tip_size, height=tip_size*2, 3323 axis=(0,-1,0), c=yline_color, res=12) 3324 else: 3325 cy = shapes.Cone((0,dy,0), r=tip_size, height=tip_size*2, 3326 axis=(0,1,0), c=yline_color, res=12) 3327 if xyshift: cy.shift(0,0,xyshift*dz) 3328 if yzshift: cy.shift(yzshift*dx,0,0) 3329 if yshift_along_x: cy.shift(yshift_along_x*dx,0,0) 3330 if yshift_along_z: cy.shift(0,0,yshift_along_z*dz) 3331 cy.name = "yTipCone" 3332 cones.append(cy) 3333 3334 if ztitle: 3335 if z_inverted: 3336 cz = shapes.Cone(r=tip_size, height=tip_size*2, 3337 axis=(0,0,-1), c=zline_color, res=12) 3338 else: 3339 cz = shapes.Cone((0,0,dz), r=tip_size, height=tip_size*2, 3340 axis=(0,0,1), c=zline_color, res=12) 3341 if yzshift: cz.shift(yzshift*dx,0,0) 3342 if zxshift: cz.shift(0,zxshift*dy,0) 3343 if zshift_along_x: cz.shift(zshift_along_x*dx,0,0) 3344 if zshift_along_y: cz.shift(0,zshift_along_y*dy,0) 3345 cz.name = "zTipCone" 3346 cones.append(cz) 3347 3348 ################################################################# MAJOR ticks 3349 majorticks, minorticks = [], [] 3350 xticks, yticks, zticks = [], [], [] 3351 if show_ticks: 3352 if xtitle: 3353 tickThickness = xtick_thickness * gscale / 2 3354 tickLength = xtick_length * gscale / 2 3355 for i in range(1, len(xticks_float) - 1): 3356 v1 = (xticks_float[i] - tickThickness, -tickLength, 0) 3357 v2 = (xticks_float[i] + tickThickness, tickLength, 0) 3358 xticks.append(shapes.Rectangle(v1, v2)) 3359 if len(xticks) > 1: 3360 xmajticks = merge(xticks).c(xlabel_color) 3361 if xaxis_rotation: 3362 xmajticks.RotateX(xaxis_rotation) 3363 if xyshift: xmajticks.shift(0,0,xyshift*dz) 3364 if zxshift: xmajticks.shift(0,zxshift*dy,0) 3365 if xshift_along_y: xmajticks.shift(0,xshift_along_y*dy,0) 3366 if xshift_along_z: xmajticks.shift(0,0,xshift_along_z*dz) 3367 xmajticks.name = "xMajorTicks" 3368 majorticks.append(xmajticks) 3369 if ytitle: 3370 tickThickness = ytick_thickness * gscale / 2 3371 tickLength = ytick_length * gscale / 2 3372 for i in range(1, len(yticks_float) - 1): 3373 v1 = (-tickLength, yticks_float[i] - tickThickness, 0) 3374 v2 = (tickLength, yticks_float[i] + tickThickness, 0) 3375 yticks.append(shapes.Rectangle(v1, v2)) 3376 if len(yticks) > 1: 3377 ymajticks = merge(yticks).c(ylabel_color) 3378 if yaxis_rotation: 3379 ymajticks.RotateY(yaxis_rotation) 3380 if xyshift: ymajticks.shift(0,0,xyshift*dz) 3381 if yzshift: ymajticks.shift(yzshift*dx,0,0) 3382 if yshift_along_x: ymajticks.shift(yshift_along_x*dx,0,0) 3383 if yshift_along_z: ymajticks.shift(0,0,yshift_along_z*dz) 3384 ymajticks.name = "yMajorTicks" 3385 majorticks.append(ymajticks) 3386 if ztitle: 3387 tickThickness = ztick_thickness * gscale / 2 3388 tickLength = ztick_length * gscale / 2.85 3389 for i in range(1, len(zticks_float) - 1): 3390 v1 = (zticks_float[i] - tickThickness, -tickLength, 0) 3391 v2 = (zticks_float[i] + tickThickness, tickLength, 0) 3392 zticks.append(shapes.Rectangle(v1, v2)) 3393 if len(zticks) > 1: 3394 zmajticks = merge(zticks).c(zlabel_color) 3395 zmajticks.RotateZ(-45 + zaxis_rotation) 3396 zmajticks.RotateY(-90) 3397 if yzshift: zmajticks.shift(yzshift*dx,0,0) 3398 if zxshift: zmajticks.shift(0,zxshift*dy,0) 3399 if zshift_along_x: zmajticks.shift(zshift_along_x*dx,0,0) 3400 if zshift_along_y: zmajticks.shift(0,zshift_along_y*dy,0) 3401 zmajticks.name = "zMajorTicks" 3402 majorticks.append(zmajticks) 3403 3404 ############################################################# MINOR ticks 3405 if xtitle and xminor_ticks and len(xticks) > 1: 3406 tickThickness = xtick_thickness * gscale / 4 3407 tickLength = xtick_length * gscale / 4 3408 xminor_ticks += 1 3409 ticks = [] 3410 for i in range(1, len(xticks)): 3411 t0, t1 = xticks[i - 1].pos(), xticks[i].pos() 3412 dt = t1 - t0 3413 for j in range(1, xminor_ticks): 3414 mt = dt * (j / xminor_ticks) + t0 3415 v1 = (mt[0] - tickThickness, -tickLength, 0) 3416 v2 = (mt[0] + tickThickness, tickLength, 0) 3417 ticks.append(shapes.Rectangle(v1, v2)) 3418 3419 # finish off the fist lower range from start to first tick 3420 t0, t1 = xticks[0].pos(), xticks[1].pos() 3421 dt = t1 - t0 3422 for j in range(1, xminor_ticks): 3423 mt = t0 - dt * (j / xminor_ticks) 3424 if mt[0] < 0: 3425 break 3426 v1 = (mt[0] - tickThickness, -tickLength, 0) 3427 v2 = (mt[0] + tickThickness, tickLength, 0) 3428 ticks.append(shapes.Rectangle(v1, v2)) 3429 3430 # finish off the last upper range from last tick to end 3431 t0, t1 = xticks[-2].pos(), xticks[-1].pos() 3432 dt = t1 - t0 3433 for j in range(1, xminor_ticks): 3434 mt = t1 + dt * (j / xminor_ticks) 3435 if mt[0] > dx: 3436 break 3437 v1 = (mt[0] - tickThickness, -tickLength, 0) 3438 v2 = (mt[0] + tickThickness, tickLength, 0) 3439 ticks.append(shapes.Rectangle(v1, v2)) 3440 3441 if ticks: 3442 xminticks = merge(ticks).c(xlabel_color) 3443 if xaxis_rotation: 3444 xminticks.RotateX(xaxis_rotation) 3445 if xyshift: xminticks.shift(0,0,xyshift*dz) 3446 if zxshift: xminticks.shift(0,zxshift*dy,0) 3447 if xshift_along_y: xminticks.shift(0,xshift_along_y*dy,0) 3448 if xshift_along_z: xminticks.shift(0,0,xshift_along_z*dz) 3449 xminticks.name = "xMinorTicks" 3450 minorticks.append(xminticks) 3451 3452 if ytitle and yminor_ticks and len(yticks) > 1: ##### y 3453 tickThickness = ytick_thickness * gscale / 4 3454 tickLength = ytick_length * gscale / 4 3455 yminor_ticks += 1 3456 ticks = [] 3457 for i in range(1, len(yticks)): 3458 t0, t1 = yticks[i - 1].pos(), yticks[i].pos() 3459 dt = t1 - t0 3460 for j in range(1, yminor_ticks): 3461 mt = dt * (j / yminor_ticks) + t0 3462 v1 = (-tickLength, mt[1] - tickThickness, 0) 3463 v2 = (tickLength, mt[1] + tickThickness, 0) 3464 ticks.append(shapes.Rectangle(v1, v2)) 3465 3466 # finish off the fist lower range from start to first tick 3467 t0, t1 = yticks[0].pos(), yticks[1].pos() 3468 dt = t1 - t0 3469 for j in range(1, yminor_ticks): 3470 mt = t0 - dt * (j / yminor_ticks) 3471 if mt[1] < 0: 3472 break 3473 v1 = (-tickLength, mt[1] - tickThickness, 0) 3474 v2 = (tickLength, mt[1] + tickThickness, 0) 3475 ticks.append(shapes.Rectangle(v1, v2)) 3476 3477 # finish off the last upper range from last tick to end 3478 t0, t1 = yticks[-2].pos(), yticks[-1].pos() 3479 dt = t1 - t0 3480 for j in range(1, yminor_ticks): 3481 mt = t1 + dt * (j / yminor_ticks) 3482 if mt[1] > dy: 3483 break 3484 v1 = (-tickLength, mt[1] - tickThickness, 0) 3485 v2 = (tickLength, mt[1] + tickThickness, 0) 3486 ticks.append(shapes.Rectangle(v1, v2)) 3487 3488 if ticks: 3489 yminticks = merge(ticks).c(ylabel_color) 3490 if yaxis_rotation: 3491 yminticks.RotateY(yaxis_rotation) 3492 if xyshift: yminticks.shift(0,0,xyshift*dz) 3493 if yzshift: yminticks.shift(yzshift*dx,0,0) 3494 if yshift_along_x: yminticks.shift(yshift_along_x*dx,0,0) 3495 if yshift_along_z: yminticks.shift(0,0,yshift_along_z*dz) 3496 yminticks.name = "yMinorTicks" 3497 minorticks.append(yminticks) 3498 3499 if ztitle and zminor_ticks and len(zticks) > 1: ##### z 3500 tickThickness = ztick_thickness * gscale / 4 3501 tickLength = ztick_length * gscale / 5 3502 zminor_ticks += 1 3503 ticks = [] 3504 for i in range(1, len(zticks)): 3505 t0, t1 = zticks[i - 1].pos(), zticks[i].pos() 3506 dt = t1 - t0 3507 for j in range(1, zminor_ticks): 3508 mt = dt * (j / zminor_ticks) + t0 3509 v1 = (mt[0] - tickThickness, -tickLength, 0) 3510 v2 = (mt[0] + tickThickness, tickLength, 0) 3511 ticks.append(shapes.Rectangle(v1, v2)) 3512 3513 # finish off the fist lower range from start to first tick 3514 t0, t1 = zticks[0].pos(), zticks[1].pos() 3515 dt = t1 - t0 3516 for j in range(1, zminor_ticks): 3517 mt = t0 - dt * (j / zminor_ticks) 3518 if mt[0] < 0: 3519 break 3520 v1 = (mt[0] - tickThickness, -tickLength, 0) 3521 v2 = (mt[0] + tickThickness, tickLength, 0) 3522 ticks.append(shapes.Rectangle(v1, v2)) 3523 3524 # finish off the last upper range from last tick to end 3525 t0, t1 = zticks[-2].pos(), zticks[-1].pos() 3526 dt = t1 - t0 3527 for j in range(1, zminor_ticks): 3528 mt = t1 + dt * (j / zminor_ticks) 3529 if mt[0] > dz: 3530 break 3531 v1 = (mt[0] - tickThickness, -tickLength, 0) 3532 v2 = (mt[0] + tickThickness, tickLength, 0) 3533 ticks.append(shapes.Rectangle(v1, v2)) 3534 3535 if ticks: 3536 zminticks = merge(ticks).c(zlabel_color) 3537 zminticks.RotateZ(-45 + zaxis_rotation) 3538 zminticks.RotateY(-90) 3539 if yzshift: zminticks.shift(yzshift*dx,0,0) 3540 if zxshift: zminticks.shift(0,zxshift*dy,0) 3541 if zshift_along_x: zminticks.shift(zshift_along_x*dx,0,0) 3542 if zshift_along_y: zminticks.shift(0,zshift_along_y*dy,0) 3543 zminticks.name = "zMinorTicks" 3544 minorticks.append(zminticks) 3545 3546 ################################################ axes NUMERIC text labels 3547 labels = [] 3548 xlab, ylab, zlab = None, None, None 3549 3550 if xlabel_size and xtitle: 3551 3552 xRot, yRot, zRot = 0, 0, 0 3553 if utils.is_sequence(xlabel_rotation): # unpck 3 rotations 3554 zRot, xRot, yRot = xlabel_rotation 3555 else: 3556 zRot = xlabel_rotation 3557 if zRot < 0: # deal with negative angles 3558 zRot += 360 3559 3560 jus = "center-top" 3561 if zRot: 3562 if zRot > 24: jus = "top-right" 3563 if zRot > 67: jus = "center-right" 3564 if zRot > 112: jus = "right-bottom" 3565 if zRot > 157: jus = "center-bottom" 3566 if zRot > 202: jus = "bottom-left" 3567 if zRot > 247: jus = "center-left" 3568 if zRot > 292: jus = "top-left" 3569 if zRot > 337: jus = "top-center" 3570 if xlabel_justify is not None: 3571 jus = xlabel_justify 3572 3573 for i in range(1, len(xticks_str)): 3574 t = xticks_str[i] 3575 if not t: 3576 continue 3577 if utils.is_sequence(xlabel_offset): 3578 xoffs, yoffs, zoffs = xlabel_offset 3579 else: 3580 xoffs, yoffs, zoffs = 0, xlabel_offset, 0 3581 xlab = shapes.Text3D( 3582 t, s=xlabel_size * text_scale * gscale, font=label_font, justify=jus 3583 ) 3584 tb = xlab.ybounds() # must be ybounds: height of char 3585 v = (xticks_float[i], 0, 0) 3586 offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0]) 3587 xlab.pos(v + offs) 3588 if xaxis_rotation: 3589 xlab.rotate_x(xaxis_rotation) 3590 if zRot: xlab.RotateZ(zRot) 3591 if xRot: xlab.RotateX(xRot) 3592 if yRot: xlab.RotateY(yRot) 3593 if xyshift: xlab.shift(0,0,xyshift*dz) 3594 if zxshift: xlab.shift(0,zxshift*dy,0) 3595 if xshift_along_y: xlab.shift(0,xshift_along_y*dy,0) 3596 if xshift_along_z: xlab.shift(0,0,xshift_along_z*dz) 3597 xlab.name = f"xNumericLabel{i}" 3598 xlab.SetUseBounds(x_use_bounds) 3599 labels.append(xlab.c(xlabel_color)) 3600 3601 if ylabel_size and ytitle: 3602 3603 xRot, yRot, zRot = 0, 0, 0 3604 if utils.is_sequence(ylabel_rotation): # unpck 3 rotations 3605 zRot, yRot, xRot = ylabel_rotation 3606 else: 3607 zRot = ylabel_rotation 3608 if zRot < 0: 3609 zRot += 360 # deal with negative angles 3610 3611 jus = "center-right" 3612 if zRot: 3613 if zRot > 24: jus = "bottom-right" 3614 if zRot > 67: jus = "center-bottom" 3615 if zRot > 112: jus = "left-bottom" 3616 if zRot > 157: jus = "center-left" 3617 if zRot > 202: jus = "top-left" 3618 if zRot > 247: jus = "center-top" 3619 if zRot > 292: jus = "top-right" 3620 if zRot > 337: jus = "right-center" 3621 if ylabel_justify is not None: 3622 jus = ylabel_justify 3623 3624 for i in range(1, len(yticks_str)): 3625 t = yticks_str[i] 3626 if not t: 3627 continue 3628 if utils.is_sequence(ylabel_offset): 3629 xoffs, yoffs, zoffs = ylabel_offset 3630 else: 3631 xoffs, yoffs, zoffs = ylabel_offset, 0, 0 3632 ylab = shapes.Text3D( 3633 t, s=ylabel_size * text_scale * gscale, font=label_font, justify=jus 3634 ) 3635 tb = ylab.ybounds() # must be ybounds: height of char 3636 v = (0, yticks_float[i], 0) 3637 offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0]) 3638 ylab.pos(v + offs) 3639 if yaxis_rotation: 3640 ylab.rotate_y(yaxis_rotation) 3641 if zRot: ylab.RotateZ(zRot) 3642 if yRot: ylab.RotateY(yRot) 3643 if xRot: ylab.RotateX(xRot) 3644 if xyshift: ylab.shift(0,0,xyshift*dz) 3645 if yzshift: ylab.shift(yzshift*dx,0,0) 3646 if yshift_along_x: ylab.shift(yshift_along_x*dx,0,0) 3647 if yshift_along_z: ylab.shift(0,0,yshift_along_z*dz) 3648 ylab.name = f"yNumericLabel{i}" 3649 ylab.SetUseBounds(y_use_bounds) 3650 labels.append(ylab.c(ylabel_color)) 3651 3652 if zlabel_size and ztitle: 3653 3654 xRot, yRot, zRot = 0, 0, 0 3655 if utils.is_sequence(zlabel_rotation): # unpck 3 rotations 3656 xRot, yRot, zRot = zlabel_rotation 3657 else: 3658 xRot = zlabel_rotation 3659 if xRot < 0: xRot += 360 # deal with negative angles 3660 3661 jus = "center-right" 3662 if xRot: 3663 if xRot > 24: jus = "bottom-right" 3664 if xRot > 67: jus = "center-bottom" 3665 if xRot > 112: jus = "left-bottom" 3666 if xRot > 157: jus = "center-left" 3667 if xRot > 202: jus = "top-left" 3668 if xRot > 247: jus = "center-top" 3669 if xRot > 292: jus = "top-right" 3670 if xRot > 337: jus = "right-center" 3671 if zlabel_justify is not None: 3672 jus = zlabel_justify 3673 3674 for i in range(1, len(zticks_str)): 3675 t = zticks_str[i] 3676 if not t: 3677 continue 3678 if utils.is_sequence(zlabel_offset): 3679 xoffs, yoffs, zoffs = zlabel_offset 3680 else: 3681 xoffs, yoffs, zoffs = zlabel_offset, zlabel_offset, 0 3682 zlab = shapes.Text3D( 3683 t, s=zlabel_size * text_scale * gscale, font=label_font, justify=jus 3684 ) 3685 tb = zlab.ybounds() # must be ybounds: height of char 3686 v = (0, 0, zticks_float[i]) 3687 offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0]) / 1.5 3688 angle = 90 3689 if dx: 3690 angle = np.arctan2(dy, dx) * 57.3 3691 zlab.RotateZ(angle + yRot) # vtk inverts order of rotations 3692 if xRot: 3693 zlab.RotateY(-xRot) # ..second 3694 zlab.RotateX(90 + zRot) # ..first 3695 zlab.pos(v + offs) 3696 if zaxis_rotation: 3697 zlab.rotate_z(zaxis_rotation) 3698 if yzshift: zlab.shift(yzshift*dx,0,0) 3699 if zxshift: zlab.shift(0,zxshift*dy,0) 3700 if zshift_along_x: zlab.shift(zshift_along_x*dx,0,0) 3701 if zshift_along_y: zlab.shift(0,zshift_along_y*dy,0) 3702 zlab.SetUseBounds(z_use_bounds) 3703 zlab.name = f"zNumericLabel{i}" 3704 labels.append(zlab.c(zlabel_color)) 3705 3706 ################################################ axes titles 3707 titles = [] 3708 3709 if xtitle: 3710 3711 xRot, yRot, zRot = 0, 0, 0 3712 if utils.is_sequence(xtitle_rotation): # unpack 3 rotations 3713 zRot, xRot, yRot = xtitle_rotation 3714 else: 3715 zRot = xtitle_rotation 3716 if zRot < 0: # deal with negative angles 3717 zRot += 360 3718 3719 if utils.is_sequence(xtitle_offset): 3720 xoffs, yoffs, zoffs = xtitle_offset 3721 else: 3722 xoffs, yoffs, zoffs = 0, xtitle_offset, 0 3723 3724 if xtitle_justify is not None: 3725 jus = xtitle_justify 3726 else: 3727 # find best justfication for given rotation(s) 3728 jus = "right-top" 3729 if zRot: 3730 if zRot > 24: jus = "center-right" 3731 if zRot > 67: jus = "right-bottom" 3732 if zRot > 157: jus = "bottom-left" 3733 if zRot > 202: jus = "center-left" 3734 if zRot > 247: jus = "top-left" 3735 if zRot > 337: jus = "top-right" 3736 3737 xt = shapes.Text3D( 3738 xtitle, 3739 s=xtitle_size * text_scale * gscale, 3740 font=title_font, 3741 c=xtitle_color, 3742 justify=jus, 3743 depth=title_depth, 3744 italic=xtitle_italic, 3745 ) 3746 if xtitle_backface_color: 3747 xt.backcolor(xtitle_backface_color) 3748 if zRot: 3749 xt.RotateZ(zRot) 3750 if xRot: 3751 xt.RotateX(xRot) 3752 if yRot: 3753 xt.RotateY(yRot) 3754 shift = 0 3755 if xlab: # xlab is the last created numeric text label.. 3756 lt0, lt1 = xlab.GetBounds()[2:4] 3757 shift = lt1 - lt0 3758 xt.pos( 3759 [(xoffs + xtitle_position) * dx, -(yoffs + xtick_length / 2) * dy - shift, zoffs * dz] 3760 ) 3761 if xaxis_rotation: 3762 xt.rotate_x(xaxis_rotation) 3763 if xyshift: 3764 xt.shift(0, 0, xyshift * dz) 3765 if xshift_along_y: 3766 xt.shift(0, xshift_along_y * dy, 0) 3767 if xshift_along_z: 3768 xt.shift(0, 0, xshift_along_z * dz) 3769 xt.SetUseBounds(x_use_bounds) 3770 if xtitle == " ": 3771 xt.SetUseBounds(False) 3772 xt.name = f"xtitle {xtitle}" 3773 titles.append(xt) 3774 if xtitle_box: 3775 titles.append(xt.box(scale=1.1).use_bounds(x_use_bounds)) 3776 3777 if ytitle: 3778 3779 xRot, yRot, zRot = 0, 0, 0 3780 if utils.is_sequence(ytitle_rotation): # unpck 3 rotations 3781 zRot, yRot, xRot = ytitle_rotation 3782 else: 3783 zRot = ytitle_rotation 3784 if len(ytitle) > 3: 3785 zRot += 90 3786 ytitle_position *= 0.975 3787 if zRot < 0: 3788 zRot += 360 # deal with negative angles 3789 3790 if utils.is_sequence(ytitle_offset): 3791 xoffs, yoffs, zoffs = ytitle_offset 3792 else: 3793 xoffs, yoffs, zoffs = ytitle_offset, 0, 0 3794 3795 if ytitle_justify is not None: 3796 jus = ytitle_justify 3797 else: 3798 jus = "center-right" 3799 if zRot: 3800 if zRot > 24: jus = "bottom-right" 3801 if zRot > 112: jus = "left-bottom" 3802 if zRot > 157: jus = "center-left" 3803 if zRot > 202: jus = "top-left" 3804 if zRot > 292: jus = "top-right" 3805 if zRot > 337: jus = "right-center" 3806 3807 yt = shapes.Text3D( 3808 ytitle, 3809 s=ytitle_size * text_scale * gscale, 3810 font=title_font, 3811 c=ytitle_color, 3812 justify=jus, 3813 depth=title_depth, 3814 italic=ytitle_italic, 3815 ) 3816 if ytitle_backface_color: 3817 yt.backcolor(ytitle_backface_color) 3818 3819 if zRot: yt.RotateZ(zRot) 3820 if yRot: yt.RotateY(yRot) 3821 if xRot: yt.RotateX(xRot) 3822 3823 shift = 0 3824 if ylab: # this is the last created num label.. 3825 lt0, lt1 = ylab.GetBounds()[0:2] 3826 shift = lt1 - lt0 3827 3828 yt.pos(-(xoffs + ytick_length / 2) * dx - shift, (yoffs + ytitle_position) * dy, zoffs * dz) 3829 if yaxis_rotation: 3830 yt.rotate_y(yaxis_rotation) 3831 if xyshift: yt.shift(0, 0, xyshift*dz) 3832 if yshift_along_x: yt.shift(yshift_along_x*dx, 0, 0) 3833 if yshift_along_z: yt.shift(0, 0, yshift_along_z*dz) 3834 yt.SetUseBounds(y_use_bounds) 3835 if ytitle == " ": 3836 yt.SetUseBounds(False) 3837 yt.name = f"ytitle {ytitle}" 3838 titles.append(yt) 3839 if ytitle_box: 3840 titles.append(yt.box(scale=1.1).use_bounds(y_use_bounds)) 3841 3842 if ztitle: 3843 3844 xRot, yRot, zRot = 0, 0, 0 3845 if utils.is_sequence(ztitle_rotation): # unpck 3 rotations 3846 xRot, yRot, zRot = ztitle_rotation 3847 else: 3848 xRot = ztitle_rotation 3849 if len(ztitle) > 3: 3850 xRot += 90 3851 ztitle_position *= 0.975 3852 if xRot < 0: 3853 xRot += 360 # deal with negative angles 3854 3855 if ztitle_justify is not None: 3856 jus = ztitle_justify 3857 else: 3858 jus = "center-right" 3859 if xRot: 3860 if xRot > 24: jus = "bottom-right" 3861 if xRot > 112: jus = "left-bottom" 3862 if xRot > 157: jus = "center-left" 3863 if xRot > 202: jus = "top-left" 3864 if xRot > 292: jus = "top-right" 3865 if xRot > 337: jus = "right-center" 3866 3867 zt = shapes.Text3D( 3868 ztitle, 3869 s=ztitle_size * text_scale * gscale, 3870 font=title_font, 3871 c=ztitle_color, 3872 justify=jus, 3873 depth=title_depth, 3874 italic=ztitle_italic, 3875 ) 3876 if ztitle_backface_color: 3877 zt.backcolor(ztitle_backface_color) 3878 3879 angle = 90 3880 if dx: 3881 angle = np.arctan2(dy, dx) * 57.3 3882 zt.RotateZ(angle + yRot) # vtk inverts order of rotations 3883 if xRot: 3884 zt.RotateY(-xRot) # ..second 3885 zt.RotateX(90 + zRot) # ..first 3886 3887 shift = 0 3888 if zlab: # this is the last created one.. 3889 lt0, lt1 = zlab.GetBounds()[0:2] 3890 shift = lt1 - lt0 3891 zt.pos( 3892 -(ztitle_offset + ztick_length / 5) * dx - shift, 3893 -(ztitle_offset + ztick_length / 5) * dy - shift, 3894 ztitle_position * dz, 3895 ) 3896 if zaxis_rotation: 3897 zt.rotate_z(zaxis_rotation) 3898 if zxshift: zt.shift(0,zxshift*dy,0) 3899 if zshift_along_x: zt.shift(zshift_along_x*dx,0,0) 3900 if zshift_along_y: zt.shift(0,zshift_along_y*dy,0) 3901 zt.SetUseBounds(z_use_bounds) 3902 if ztitle == " ": 3903 zt.SetUseBounds(False) 3904 zt.name = f"ztitle {ztitle}" 3905 titles.append(zt) 3906 3907 ################################################### header title 3908 if htitle: 3909 if htitle_font is None: 3910 htitle_font = title_font 3911 if htitle_color is None: 3912 htitle_color = xtitle_color 3913 htit = shapes.Text3D( 3914 htitle, 3915 s=htitle_size * gscale * text_scale, 3916 font=htitle_font, 3917 c=htitle_color, 3918 justify=htitle_justify, 3919 depth=title_depth, 3920 italic=htitle_italic, 3921 ) 3922 if htitle_rotation: 3923 htit.RotateX(htitle_rotation) 3924 wpos = [(0.5 + htitle_offset[0]) * dx, (1 + htitle_offset[1]) * dy, htitle_offset[2] * dz] 3925 htit.pos(wpos) 3926 if xyshift: 3927 htit.shift(0, 0, xyshift * dz) 3928 htit.name = f"htitle {htitle}" 3929 titles.append(htit) 3930 3931 ###### 3932 acts = titles + lines + labels + grids + framelines 3933 acts += highlights + majorticks + minorticks + cones 3934 orig = (min_bns[0], min_bns[2], min_bns[4]) 3935 for a in acts: 3936 a.PickableOff() 3937 a.AddPosition(orig) 3938 a.GetProperty().LightingOff() 3939 asse = Assembly(acts) 3940 asse.SetOrigin(orig) 3941 asse.PickableOff() 3942 asse.name = "Axes" 3943 return asse 3944 3945 3946def add_global_axes(axtype=None, c=None, bounds=()): 3947 """ 3948 Draw axes on scene. Available axes types are 3949 3950 Parameters 3951 ---------- 3952 axtype : (int) 3953 - 0, no axes, 3954 - 1, draw three gray grid walls 3955 - 2, show cartesian axes from (0,0,0) 3956 - 3, show positive range of cartesian axes from (0,0,0) 3957 - 4, show a triad at bottom left 3958 - 5, show a cube at bottom left 3959 - 6, mark the corners of the bounding box 3960 - 7, draw a 3D ruler at each side of the cartesian axes 3961 - 8, show the `vtkCubeAxesActor` object 3962 - 9, show the bounding box outLine 3963 - 10, show three circles representing the maximum bounding box 3964 - 11, show a large grid on the x-y plane (use with zoom=8) 3965 - 12, show polar axes 3966 - 13, draw a simple ruler at the bottom of the window 3967 - 14, show the vtk default `vtkCameraOrientationWidget` object 3968 3969 Axis type-1 can be fully customized by passing a dictionary `axes=dict()`, 3970 see `vedo.Axes` for the complete list of options. 3971 3972 Example 3973 ------- 3974 .. code-block:: python 3975 3976 from vedo import Box, show 3977 b = Box(pos=(0, 0, 0), length=80, width=90, height=70).alpha(0.1) 3978 show( 3979 b, 3980 axes={ 3981 "xtitle": "Some long variable [a.u.]", 3982 "number_of_divisions": 4, 3983 # ... 3984 }, 3985 ) 3986 """ 3987 plt = vedo.plotter_instance 3988 if axtype is not None: 3989 plt.axes = axtype # override 3990 3991 r = plt.renderers.index(plt.renderer) 3992 3993 if not plt.axes: 3994 return 3995 3996 if c is None: # automatic black or white 3997 c = (0.9, 0.9, 0.9) 3998 if np.sum(plt.renderer.GetBackground()) > 1.5: 3999 c = (0.1, 0.1, 0.1) 4000 else: 4001 c = get_color(c) # for speed 4002 4003 if not plt.renderer: 4004 return None 4005 4006 if plt.axes_instances[r]: 4007 return None 4008 4009 ############################################################ 4010 # custom grid walls 4011 if plt.axes == 1 or plt.axes is True or isinstance(plt.axes, dict): 4012 4013 if len(bounds) == 6: 4014 bnds = bounds 4015 xrange = (bnds[0], bnds[1]) 4016 yrange = (bnds[2], bnds[3]) 4017 zrange = (bnds[4], bnds[5]) 4018 else: 4019 xrange=None 4020 yrange=None 4021 zrange=None 4022 4023 if isinstance(plt.axes, dict): 4024 plt.axes.update({"use_global": True}) 4025 # protect from invalid camelCase options from vedo<=2.3 4026 for k in plt.axes: 4027 if k.lower() != k: 4028 return 4029 if "xrange" in plt.axes: 4030 xrange = plt.axes.pop("xrange") 4031 if "yrange" in plt.axes: 4032 yrange = plt.axes.pop("yrange") 4033 if "zrange" in plt.axes: 4034 zrange = plt.axes.pop("zrange") 4035 asse = Axes(**plt.axes, xrange=xrange, yrange=yrange, zrange=zrange) 4036 else: 4037 asse = Axes(xrange=xrange, yrange=yrange, zrange=zrange) 4038 4039 plt.renderer.AddActor(asse) 4040 plt.axes_instances[r] = asse 4041 4042 elif plt.axes in (2, 3): 4043 x0, x1, y0, y1, z0, z1 = plt.renderer.ComputeVisiblePropBounds() 4044 xcol, ycol, zcol = "dr", "dg", "db" 4045 s = 1 4046 alpha = 1 4047 centered = False 4048 dx, dy, dz = x1 - x0, y1 - y0, z1 - z0 4049 aves = np.sqrt(dx * dx + dy * dy + dz * dz) / 2 4050 x0, x1 = min(x0, 0), max(x1, 0) 4051 y0, y1 = min(y0, 0), max(y1, 0) 4052 z0, z1 = min(z0, 0), max(z1, 0) 4053 4054 if plt.axes == 3: 4055 if x1 > 0: 4056 x0 = 0 4057 if y1 > 0: 4058 y0 = 0 4059 if z1 > 0: 4060 z0 = 0 4061 4062 dx, dy, dz = x1 - x0, y1 - y0, z1 - z0 4063 acts = [] 4064 if x0 * x1 <= 0 or y0 * z1 <= 0 or z0 * z1 <= 0: # some ranges contain origin 4065 zero = shapes.Sphere(r=aves / 120 * s, c="k", alpha=alpha, res=10) 4066 acts += [zero] 4067 4068 if dx > aves / 100: 4069 xl = shapes.Cylinder([[x0, 0, 0], [x1, 0, 0]], r=aves / 250 * s, c=xcol, alpha=alpha) 4070 xc = shapes.Cone( 4071 pos=[x1, 0, 0], 4072 c=xcol, 4073 alpha=alpha, 4074 r=aves / 100 * s, 4075 height=aves / 25 * s, 4076 axis=[1, 0, 0], 4077 res=10, 4078 ) 4079 wpos = [x1, -aves / 25 * s, 0] # aligned to arrow tip 4080 if centered: 4081 wpos = [(x0 + x1) / 2, -aves / 25 * s, 0] 4082 xt = shapes.Text3D("x", pos=wpos, s=aves / 40 * s, c=xcol) 4083 acts += [xl, xc, xt] 4084 4085 if dy > aves / 100: 4086 yl = shapes.Cylinder([[0, y0, 0], [0, y1, 0]], r=aves / 250 * s, c=ycol, alpha=alpha) 4087 yc = shapes.Cone( 4088 pos=[0, y1, 0], 4089 c=ycol, 4090 alpha=alpha, 4091 r=aves / 100 * s, 4092 height=aves / 25 * s, 4093 axis=[0, 1, 0], 4094 res=10, 4095 ) 4096 wpos = [-aves / 40 * s, y1, 0] 4097 if centered: 4098 wpos = [-aves / 40 * s, (y0 + y1) / 2, 0] 4099 yt = shapes.Text3D("y", pos=(0, 0, 0), s=aves / 40 * s, c=ycol) 4100 yt.pos(wpos).RotateZ(90) 4101 acts += [yl, yc, yt] 4102 4103 if dz > aves / 100: 4104 zl = shapes.Cylinder([[0, 0, z0], [0, 0, z1]], r=aves / 250 * s, c=zcol, alpha=alpha) 4105 zc = shapes.Cone( 4106 pos=[0, 0, z1], 4107 c=zcol, 4108 alpha=alpha, 4109 r=aves / 100 * s, 4110 height=aves / 25 * s, 4111 axis=[0, 0, 1], 4112 res=10, 4113 ) 4114 wpos = [-aves / 50 * s, -aves / 50 * s, z1] 4115 if centered: 4116 wpos = [-aves / 50 * s, -aves / 50 * s, (z0 + z1) / 2] 4117 zt = shapes.Text3D("z", pos=(0, 0, 0), s=aves / 40 * s, c=zcol) 4118 zt.pos(wpos).RotateZ(45) 4119 zt.RotateX(90) 4120 acts += [zl, zc, zt] 4121 for a in acts: 4122 a.PickableOff() 4123 ass = Assembly(acts) 4124 ass.PickableOff() 4125 plt.renderer.AddActor(ass) 4126 plt.axes_instances[r] = ass 4127 4128 elif plt.axes == 4: 4129 axact = vtk.vtkAxesActor() 4130 axact.SetShaftTypeToCylinder() 4131 axact.SetCylinderRadius(0.03) 4132 axact.SetXAxisLabelText("x") 4133 axact.SetYAxisLabelText("y") 4134 axact.SetZAxisLabelText("z") 4135 axact.GetXAxisShaftProperty().SetColor(1, 0, 0) 4136 axact.GetYAxisShaftProperty().SetColor(0, 1, 0) 4137 axact.GetZAxisShaftProperty().SetColor(0, 0, 1) 4138 axact.GetXAxisTipProperty().SetColor(1, 0, 0) 4139 axact.GetYAxisTipProperty().SetColor(0, 1, 0) 4140 axact.GetZAxisTipProperty().SetColor(0, 0, 1) 4141 bc = np.array(plt.renderer.GetBackground()) 4142 if np.sum(bc) < 1.5: 4143 lc = (1, 1, 1) 4144 else: 4145 lc = (0, 0, 0) 4146 axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() 4147 axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() 4148 axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() 4149 axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff() 4150 axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff() 4151 axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff() 4152 axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff() 4153 axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff() 4154 axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff() 4155 axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc) 4156 axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc) 4157 axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc) 4158 axact.PickableOff() 4159 icn = Icon(axact, size=0.1) 4160 plt.axes_instances[r] = icn 4161 icn.SetInteractor(plt.interactor) 4162 icn.EnabledOn() 4163 icn.InteractiveOff() 4164 plt.widgets.append(icn) 4165 4166 elif plt.axes == 5: 4167 axact = vtk.vtkAnnotatedCubeActor() 4168 axact.GetCubeProperty().SetColor(get_color(settings.annotated_cube_color)) 4169 axact.SetTextEdgesVisibility(0) 4170 axact.SetFaceTextScale(settings.annotated_cube_text_scale) 4171 axact.SetXPlusFaceText(settings.annotated_cube_texts[0]) # XPlus 4172 axact.SetXMinusFaceText(settings.annotated_cube_texts[1]) # XMinus 4173 axact.SetYPlusFaceText(settings.annotated_cube_texts[2]) # YPlus 4174 axact.SetYMinusFaceText(settings.annotated_cube_texts[3]) # YMinus 4175 axact.SetZPlusFaceText(settings.annotated_cube_texts[4]) # ZPlus 4176 axact.SetZMinusFaceText(settings.annotated_cube_texts[5]) # ZMinus 4177 axact.SetZFaceTextRotation(90) 4178 4179 if settings.annotated_cube_text_color is None: # use default 4180 axact.GetXPlusFaceProperty().SetColor(get_color("r")) 4181 axact.GetXMinusFaceProperty().SetColor(get_color("dr")) 4182 axact.GetYPlusFaceProperty().SetColor(get_color("g")) 4183 axact.GetYMinusFaceProperty().SetColor(get_color("dg")) 4184 axact.GetZPlusFaceProperty().SetColor(get_color("b")) 4185 axact.GetZMinusFaceProperty().SetColor(get_color("db")) 4186 else: # use single user color 4187 ac = get_color(settings.annotated_cube_text_color) 4188 axact.GetXPlusFaceProperty().SetColor(ac) 4189 axact.GetXMinusFaceProperty().SetColor(ac) 4190 axact.GetYPlusFaceProperty().SetColor(ac) 4191 axact.GetYMinusFaceProperty().SetColor(ac) 4192 axact.GetZPlusFaceProperty().SetColor(ac) 4193 axact.GetZMinusFaceProperty().SetColor(ac) 4194 4195 axact.PickableOff() 4196 icn = Icon(axact, size=0.06) 4197 plt.axes_instances[r] = icn 4198 icn.SetInteractor(plt.interactor) 4199 icn.EnabledOn() 4200 icn.InteractiveOff() 4201 plt.widgets.append(icn) 4202 4203 elif plt.axes == 6: 4204 ocf = vtk.vtkOutlineCornerFilter() 4205 ocf.SetCornerFactor(0.1) 4206 largestact, sz = None, -1 4207 for a in plt.actors: 4208 if a.GetPickable(): 4209 b = a.GetBounds() 4210 if b is None: 4211 return 4212 d = max(b[1] - b[0], b[3] - b[2], b[5] - b[4]) 4213 if sz < d: 4214 largestact = a 4215 sz = d 4216 if isinstance(largestact, Assembly): 4217 ocf.SetInputData(largestact.unpack(0).GetMapper().GetInput()) 4218 else: 4219 ocf.SetInputData(largestact.GetMapper().GetInput()) 4220 ocf.Update() 4221 oc_mapper = vtk.vtkHierarchicalPolyDataMapper() 4222 oc_mapper.SetInputConnection(0, ocf.GetOutputPort(0)) 4223 oc_actor = vtk.vtkActor() 4224 oc_actor.SetMapper(oc_mapper) 4225 bc = np.array(plt.renderer.GetBackground()) 4226 if np.sum(bc) < 1.5: 4227 lc = (1, 1, 1) 4228 else: 4229 lc = (0, 0, 0) 4230 oc_actor.GetProperty().SetColor(lc) 4231 oc_actor.PickableOff() 4232 oc_actor.UseBoundsOn() 4233 plt.renderer.AddActor(oc_actor) 4234 plt.axes_instances[r] = oc_actor 4235 plt.renderer.AddActor(oc_actor) 4236 4237 elif plt.axes == 7: 4238 vbb = compute_visible_bounds()[0] 4239 rulax = RulerAxes(vbb, c=c, xtitle="x - ", ytitle="y - ", ztitle="z - ") 4240 plt.axes_instances[r] = rulax 4241 if not rulax: 4242 return None 4243 rulax.UseBoundsOn() 4244 rulax.PickableOff() 4245 plt.renderer.AddActor(rulax) 4246 4247 elif plt.axes == 8: 4248 vbb = compute_visible_bounds()[0] 4249 ca = vtk.vtkCubeAxesActor() 4250 ca.SetBounds(vbb) 4251 ca.SetCamera(plt.renderer.GetActiveCamera()) 4252 ca.GetXAxesLinesProperty().SetColor(c) 4253 ca.GetYAxesLinesProperty().SetColor(c) 4254 ca.GetZAxesLinesProperty().SetColor(c) 4255 for i in range(3): 4256 ca.GetLabelTextProperty(i).SetColor(c) 4257 ca.GetTitleTextProperty(i).SetColor(c) 4258 ca.SetTitleOffset(5) 4259 ca.SetFlyMode(3) 4260 ca.SetXTitle("x") 4261 ca.SetYTitle("y") 4262 ca.SetZTitle("z") 4263 ca.PickableOff() 4264 ca.UseBoundsOff() 4265 plt.axes_instances[r] = ca 4266 plt.renderer.AddActor(ca) 4267 4268 elif plt.axes == 9: 4269 vbb = compute_visible_bounds()[0] 4270 src = vtk.vtkCubeSource() 4271 src.SetXLength(vbb[1] - vbb[0]) 4272 src.SetYLength(vbb[3] - vbb[2]) 4273 src.SetZLength(vbb[5] - vbb[4]) 4274 src.Update() 4275 ca = Mesh(src.GetOutput(), c, 0.5).wireframe(True) 4276 ca.pos((vbb[0] + vbb[1]) / 2, (vbb[3] + vbb[2]) / 2, (vbb[5] + vbb[4]) / 2) 4277 ca.PickableOff() 4278 ca.UseBoundsOff() 4279 plt.axes_instances[r] = ca 4280 plt.renderer.AddActor(ca) 4281 4282 elif plt.axes == 10: 4283 vbb = compute_visible_bounds()[0] 4284 x0 = (vbb[0] + vbb[1]) / 2, (vbb[3] + vbb[2]) / 2, (vbb[5] + vbb[4]) / 2 4285 rx, ry, rz = (vbb[1] - vbb[0]) / 2, (vbb[3] - vbb[2]) / 2, (vbb[5] - vbb[4]) / 2 4286 rm = max(rx, ry, rz) 4287 xc = shapes.Disc(x0, r1=rm, r2=rm, c="lr", res=(1, 72)) 4288 yc = shapes.Disc(x0, r1=rm, r2=rm, c="lg", res=(1, 72)) 4289 yc.RotateX(90) 4290 zc = shapes.Disc(x0, r1=rm, r2=rm, c="lb", res=(1, 72)) 4291 yc.RotateY(90) 4292 xc.clean().alpha(0.5).wireframe().linewidth(2).PickableOff() 4293 yc.clean().alpha(0.5).wireframe().linewidth(2).PickableOff() 4294 zc.clean().alpha(0.5).wireframe().linewidth(2).PickableOff() 4295 ca = xc + yc + zc 4296 ca.PickableOff() 4297 ca.UseBoundsOn() 4298 plt.renderer.AddActor(ca) 4299 plt.axes_instances[r] = ca 4300 4301 elif plt.axes == 11: 4302 vbb, ss = compute_visible_bounds()[0:2] 4303 xpos, ypos = (vbb[1] + vbb[0]) / 2, (vbb[3] + vbb[2]) / 2 4304 gs = sum(ss) * 3 4305 gr = shapes.Grid((xpos, ypos, vbb[4]), s=(gs, gs), res=(11, 11), c=c, alpha=0.1) 4306 gr.lighting("off").PickableOff() 4307 gr.UseBoundsOff() 4308 plt.axes_instances[r] = gr 4309 plt.renderer.AddActor(gr) 4310 4311 elif plt.axes == 12: 4312 polaxes = vtk.vtkPolarAxesActor() 4313 vbb = compute_visible_bounds()[0] 4314 4315 polaxes.SetPolarAxisTitle("radial distance") 4316 polaxes.SetPole(0, 0, vbb[4]) 4317 rd = max(abs(vbb[0]), abs(vbb[2]), abs(vbb[1]), abs(vbb[3])) 4318 polaxes.SetMaximumRadius(rd) 4319 polaxes.AutoSubdividePolarAxisOff() 4320 polaxes.SetNumberOfPolarAxisTicks(10) 4321 polaxes.SetCamera(plt.renderer.GetActiveCamera()) 4322 polaxes.SetPolarLabelFormat("%6.1f") 4323 polaxes.PolarLabelVisibilityOff() # due to bad overlap of labels 4324 4325 polaxes.GetPolarArcsProperty().SetColor(c) 4326 polaxes.GetPolarAxisProperty().SetColor(c) 4327 polaxes.GetPolarAxisTitleTextProperty().SetColor(c) 4328 polaxes.GetPolarAxisLabelTextProperty().SetColor(c) 4329 polaxes.GetLastRadialAxisTextProperty().SetColor(c) 4330 polaxes.GetSecondaryRadialAxesTextProperty().SetColor(c) 4331 polaxes.GetSecondaryRadialAxesProperty().SetColor(c) 4332 polaxes.GetSecondaryPolarArcsProperty().SetColor(c) 4333 4334 polaxes.SetMinimumAngle(0.0) 4335 polaxes.SetMaximumAngle(315.0) 4336 polaxes.SetNumberOfPolarAxisTicks(5) 4337 polaxes.UseBoundsOn() 4338 polaxes.PickableOff() 4339 plt.axes_instances[r] = polaxes 4340 plt.renderer.AddActor(polaxes) 4341 4342 elif plt.axes == 13: 4343 # draws a simple ruler at the bottom of the window 4344 ls = vtk.vtkLegendScaleActor() 4345 ls.RightAxisVisibilityOff() 4346 ls.TopAxisVisibilityOff() 4347 ls.LeftAxisVisibilityOff() 4348 ls.LegendVisibilityOff() 4349 ls.SetBottomBorderOffset(50) 4350 ls.GetBottomAxis().SetNumberOfMinorTicks(1) 4351 ls.GetBottomAxis().SetFontFactor(1.1) 4352 ls.GetBottomAxis().GetProperty().SetColor(c) 4353 ls.GetBottomAxis().GetProperty().SetOpacity(1.0) 4354 ls.GetBottomAxis().GetProperty().SetLineWidth(2) 4355 ls.GetBottomAxis().GetLabelTextProperty().SetColor(c) 4356 ls.GetBottomAxis().GetLabelTextProperty().BoldOff() 4357 ls.GetBottomAxis().GetLabelTextProperty().ItalicOff() 4358 pr = ls.GetBottomAxis().GetLabelTextProperty() 4359 pr.SetFontFamily(vtk.VTK_FONT_FILE) 4360 pr.SetFontFile(utils.get_font_path(settings.default_font)) 4361 ls.PickableOff() 4362 # if not plt.renderer.GetActiveCamera().GetParallelProjection(): 4363 # vedo.logger.warning("Axes type 13 should be used with parallel projection") 4364 plt.axes_instances[r] = ls 4365 plt.renderer.AddActor(ls) 4366 4367 elif plt.axes == 14: 4368 try: 4369 cow = vtk.vtkCameraOrientationWidget() 4370 cow.SetParentRenderer(plt.renderer) 4371 cow.On() 4372 plt.axes_instances[r] = cow 4373 except AttributeError: 4374 vedo.logger.warning("axes mode 14 is unavailable in this vtk version") 4375 4376 else: 4377 e = "\bomb Keyword axes type must be in range [0-13]." 4378 e += "Available axes types are:\n\n" 4379 e += "0 = no axes\n" 4380 e += "1 = draw three customizable gray grid walls\n" 4381 e += "2 = show cartesian axes from (0,0,0)\n" 4382 e += "3 = show positive range of cartesian axes from (0,0,0)\n" 4383 e += "4 = show a triad at bottom left\n" 4384 e += "5 = show a cube at bottom left\n" 4385 e += "6 = mark the corners of the bounding box\n" 4386 e += "7 = draw a 3D ruler at each side of the cartesian axes\n" 4387 e += "8 = show the vtkCubeAxesActor object\n" 4388 e += "9 = show the bounding box outline\n" 4389 e += "10 = show three circles representing the maximum bounding box\n" 4390 e += "11 = show a large grid on the x-y plane (use with zoom=8)\n" 4391 e += "12 = show polar axes\n" 4392 e += "13 = draw a simple ruler at the bottom of the window\n" 4393 e += "14 = show the CameraOrientationWidget object" 4394 vedo.logger.warning(e) 4395 4396 if not plt.axes_instances[r]: 4397 plt.axes_instances[r] = True 4398 return None
859def ScalarBar( 860 obj, 861 title="", 862 pos=(0.8, 0.05), 863 title_yoffset=15, 864 font_size=12, 865 size=(None, None), 866 nlabels=None, 867 c="k", 868 horizontal=False, 869 use_alpha=True, 870 label_format=":6.3g", 871): 872 """ 873 A 2D scalar bar for the specified obj. 874 875 Arguments: 876 pos : (list) 877 fractional x and y position in the 2D window 878 size : (list) 879 size of the scalarbar in pixel units (width, height) 880 nlabels : (int) 881 number of numeric labels to be shown 882 use_alpha : (bool) 883 retain transparency in scalarbar 884 horizontal : (bool) 885 show in horizontal layout 886 887 Examples: 888 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 889 890 ![](https://user-images.githubusercontent.com/32848391/62940174-4bdc7900-bdd3-11e9-9713-e4f3e2fdab63.png) 891 """ 892 893 if isinstance(obj, Points): 894 vtkscalars = obj.inputdata().GetPointData().GetScalars() 895 if vtkscalars is None: 896 vtkscalars = obj.inputdata().GetCellData().GetScalars() 897 if not vtkscalars: 898 return None 899 lut = vtkscalars.GetLookupTable() 900 if not lut: 901 lut = obj.mapper().GetLookupTable() 902 if not lut: 903 return None 904 905 elif isinstance(obj, (Volume, TetMesh)): 906 lut = utils.ctf2lut(obj) 907 908 elif utils.is_sequence(obj) and len(obj) == 2: 909 x = np.linspace(obj[0], obj[1], 256) 910 data = [] 911 for i in range(256): 912 rgb = color_map(i, c, 0, 256) 913 data.append([x[i], rgb]) 914 lut = build_lut(data) 915 916 elif not hasattr(obj, "mapper"): 917 vedo.logger.error(f"in add_scalarbar(): input is invalid {type(obj)}. Skip.") 918 return None 919 920 else: 921 return None 922 923 c = get_color(c) 924 sb = vtk.vtkScalarBarActor() 925 926 # print(sb.GetLabelFormat()) 927 label_format = label_format.replace(":", "%-#") 928 sb.SetLabelFormat(label_format) 929 930 sb.SetLookupTable(lut) 931 sb.SetUseOpacity(use_alpha) 932 sb.SetDrawFrame(0) 933 sb.SetDrawBackground(0) 934 if lut.GetUseBelowRangeColor(): 935 sb.DrawBelowRangeSwatchOn() 936 sb.SetBelowRangeAnnotation("") 937 if lut.GetUseAboveRangeColor(): 938 sb.DrawAboveRangeSwatchOn() 939 sb.SetAboveRangeAnnotation("") 940 if lut.GetNanColor() != (0.5, 0.0, 0.0, 1.0): 941 sb.DrawNanAnnotationOn() 942 sb.SetNanAnnotation("nan") 943 944 if title: 945 if "\\" in repr(title): 946 for r in shapes._reps: 947 title = title.replace(r[0], r[1]) 948 titprop = sb.GetTitleTextProperty() 949 titprop.BoldOn() 950 titprop.ItalicOff() 951 titprop.ShadowOff() 952 titprop.SetColor(c) 953 titprop.SetVerticalJustificationToTop() 954 titprop.SetFontSize(font_size) 955 titprop.SetFontFamily(vtk.VTK_FONT_FILE) 956 titprop.SetFontFile(utils.get_font_path(settings.default_font)) 957 sb.SetTitle(title) 958 sb.SetVerticalTitleSeparation(title_yoffset) 959 sb.SetTitleTextProperty(titprop) 960 961 sb.UnconstrainedFontSizeOn() 962 sb.DrawAnnotationsOn() 963 sb.DrawTickLabelsOn() 964 sb.SetMaximumNumberOfColors(256) 965 966 if horizontal: 967 sb.SetOrientationToHorizontal() 968 sb.SetNumberOfLabels(3) 969 sb.SetTextPositionToSucceedScalarBar() 970 sb.SetPosition(pos) 971 sb.SetMaximumWidthInPixels(1000) 972 sb.SetMaximumHeightInPixels(50) 973 else: 974 sb.SetNumberOfLabels(7) 975 sb.SetTextPositionToPrecedeScalarBar() 976 sb.SetPosition(pos[0] + 0.09, pos[1]) 977 sb.SetMaximumWidthInPixels(60) 978 sb.SetMaximumHeightInPixels(250) 979 980 if size[0] is not None: 981 sb.SetMaximumWidthInPixels(size[0]) 982 if size[1] is not None: 983 sb.SetMaximumHeightInPixels(size[1]) 984 985 if nlabels is not None: 986 sb.SetNumberOfLabels(nlabels) 987 988 sctxt = sb.GetLabelTextProperty() 989 sctxt.SetFontFamily(vtk.VTK_FONT_FILE) 990 sctxt.SetFontFile(utils.get_font_path(settings.default_font)) 991 sctxt.SetColor(c) 992 sctxt.SetShadow(0) 993 sctxt.SetFontSize(font_size - 2) 994 sb.SetAnnotationTextProperty(sctxt) 995 sb.PickableOff() 996 return sb
A 2D scalar bar for the specified obj.
Arguments:
- pos : (list) fractional x and y position in the 2D window
- size : (list) size of the scalarbar in pixel units (width, height)
- nlabels : (int) number of numeric labels to be shown
- use_alpha : (bool) retain transparency in scalarbar
- horizontal : (bool) show in horizontal layout
Examples:
1000def ScalarBar3D( 1001 obj, 1002 title="", 1003 pos=None, 1004 size=(None, None), 1005 title_font="", 1006 title_xoffset=-1.5, 1007 title_yoffset=0.0, 1008 title_size=1.5, 1009 title_rotation=0.0, 1010 nlabels=8, 1011 label_font="", 1012 label_size=1, 1013 label_offset=0.375, 1014 label_rotation=0, 1015 label_format="", 1016 italic=0, 1017 c=None, 1018 draw_box=True, 1019 above_text=None, 1020 below_text=None, 1021 nan_text="NaN", 1022 categories=None, 1023): 1024 """ 1025 Create a 3D scalar bar for the specified object. 1026 1027 Input `obj` input can be: 1028 1029 - a list of numbers, 1030 - a list of two numbers in the form (min, max), 1031 - a Mesh already containing a set of scalars associated to vertices or cells, 1032 - if None the last object in the list of actors will be used. 1033 1034 Arguments: 1035 size : (list) 1036 (thickness, length) of scalarbar 1037 title : (str) 1038 scalar bar title 1039 title_xoffset : (float) 1040 horizontal space btw title and color scalarbar 1041 title_yoffset : (float) 1042 vertical space offset 1043 title_size : (float) 1044 size of title wrt numeric labels 1045 title_rotation : (float) 1046 title rotation in degrees 1047 nlabels : (int) 1048 number of numeric labels 1049 label_font : (str) 1050 font type for labels 1051 label_size : (float) 1052 label scale factor 1053 label_offset : (float) 1054 space btw numeric labels and scale 1055 label_rotation : (float) 1056 label rotation in degrees 1057 draw_box : (bool) 1058 draw a box around the colorbar 1059 categories : (list) 1060 make a categorical scalarbar, 1061 the input list will have the format [value, color, alpha, textlabel] 1062 1063 Examples: 1064 - [scalarbars.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/scalarbars.py) 1065 """ 1066 1067 if isinstance(obj, Points): 1068 lut = obj.mapper().GetLookupTable() 1069 if not lut or lut.GetTable().GetNumberOfTuples() == 0: 1070 # create the most similar to the default 1071 obj.cmap("jet_r") 1072 lut = obj.mapper().GetLookupTable() 1073 vmin, vmax = lut.GetRange() 1074 1075 elif isinstance(obj, (Volume, TetMesh)): 1076 lut = utils.ctf2lut(obj) 1077 vmin, vmax = lut.GetRange() 1078 1079 elif utils.is_sequence(obj): 1080 vmin, vmax = np.min(obj), np.max(obj) 1081 1082 else: 1083 vedo.logger.error("in ScalarBar3D(): input must be a vedo object with bounds.") 1084 return obj 1085 1086 bns = obj.bounds() 1087 sx, sy = size 1088 if sy is None: 1089 sy = bns[3] - bns[2] 1090 if sx is None: 1091 sx = sy / 18 1092 1093 if categories is not None: ################################ 1094 ncats = len(categories) 1095 scale = shapes.Grid([-float(sx) * label_offset, 0, 0], 1096 c=c, alpha=1, s=(sx, sy), res=(1, ncats)) 1097 cols, alphas = [], [] 1098 ticks_pos, ticks_txt = [0.0], [""] 1099 for i, cat in enumerate(categories): 1100 cl = get_color(cat[1]) 1101 cols.append(cl) 1102 if len(cat) > 2: 1103 alphas.append(cat[2]) 1104 else: 1105 alphas.append(1) 1106 if len(cat) > 3: 1107 ticks_txt.append(cat[3]) 1108 else: 1109 ticks_txt.append("") 1110 ticks_pos.append((i + 0.5) / ncats) 1111 ticks_pos.append(1.0) 1112 ticks_txt.append("") 1113 rgba = np.c_[np.array(cols) * 255, np.array(alphas) * 255] 1114 scale.cell_individual_colors(rgba) 1115 1116 else: ######################################################## 1117 1118 # build the color scale part 1119 scale = shapes.Grid( 1120 [-float(sx) * label_offset, 0, 0], 1121 c=c, 1122 alpha=1, 1123 s=(sx, sy), 1124 res=(1, lut.GetTable().GetNumberOfTuples()), 1125 ) 1126 cscals = np.linspace(vmin, vmax, lut.GetTable().GetNumberOfTuples()) 1127 1128 if lut.GetScale(): # logarithmic scale 1129 lut10 = vtk.vtkLookupTable() 1130 lut10.DeepCopy(lut) 1131 lut10.SetScaleToLinear() 1132 scale.cmap(lut10, cscals, on="cells") 1133 tk = utils.make_ticks(vmin, vmax, nlabels, logscale=True, useformat=label_format) 1134 else: 1135 scale.cmap(lut, cscals, on="cells") 1136 tk = utils.make_ticks(vmin, vmax, nlabels, logscale=False, useformat=label_format) 1137 ticks_pos, ticks_txt = tk 1138 scale.lw(0).wireframe(False).lighting("off") 1139 1140 scales = [scale] 1141 1142 xbns = scale.xbounds() 1143 if pos is None: 1144 d = sx / 2 1145 if title: 1146 d = np.sqrt((bns[1] - bns[0]) ** 2 + sy * sy) / 20 1147 pos = (bns[1] - xbns[0] + d, (bns[2] + bns[3]) / 2, bns[4]) 1148 1149 lsize = sy / 60 * label_size 1150 1151 tacts = [] 1152 for i, p in enumerate(ticks_pos): 1153 tx = ticks_txt[i] 1154 if i and tx: 1155 # build numeric text 1156 y = (p - 0.5) * sy 1157 if label_rotation: 1158 a = shapes.Text3D( 1159 tx, 1160 pos=[sx * label_offset, y, 0], 1161 s=lsize, 1162 justify="center-top", 1163 c=c, 1164 italic=italic, 1165 font=label_font, 1166 ) 1167 a.RotateZ(label_rotation) 1168 else: 1169 a = shapes.Text3D( 1170 tx, 1171 pos=[sx * label_offset, y, 0], 1172 s=lsize, 1173 justify="center-left", 1174 c=c, 1175 italic=italic, 1176 font=label_font, 1177 ) 1178 1179 tacts.append(a) 1180 1181 # build ticks 1182 tic = shapes.Line([xbns[1], y, 0], [xbns[1] + sx * label_offset / 4, y, 0], lw=2, c=c) 1183 tacts.append(tic) 1184 1185 # build title 1186 if title: 1187 t = shapes.Text3D( 1188 title, 1189 (0, 0, 0), 1190 s=sy / 50 * title_size, 1191 c=c, 1192 justify="centered", 1193 italic=italic, 1194 font=title_font, 1195 ) 1196 t.RotateZ(90 + title_rotation) 1197 t.pos(sx * title_xoffset, title_yoffset, 0) 1198 tacts.append(t) 1199 1200 # build below scale 1201 if lut.GetUseBelowRangeColor(): 1202 r, g, b, alfa = lut.GetBelowRangeColor() 1203 sx = float(sx) 1204 sy = float(sy) 1205 brect = shapes.Rectangle( 1206 [-sx * label_offset - sx / 2, -sy / 2 - sx - sx * 0.1, 0], 1207 [-sx * label_offset + sx / 2, -sy / 2 - sx * 0.1, 0], 1208 c=(r, g, b), 1209 alpha=alfa, 1210 ) 1211 brect.lw(1).lc(c).lighting("off") 1212 scales += [brect] 1213 if below_text is None: 1214 below_text = " <" + str(vmin) 1215 if below_text: 1216 if label_rotation: 1217 btx = shapes.Text3D( 1218 below_text, 1219 (0, 0, 0), 1220 s=lsize, 1221 c=c, 1222 justify="center-top", 1223 italic=italic, 1224 font=label_font, 1225 ) 1226 btx.RotateZ(label_rotation) 1227 else: 1228 btx = shapes.Text3D( 1229 below_text, 1230 (0, 0, 0), 1231 s=lsize, 1232 c=c, 1233 justify="center-left", 1234 italic=italic, 1235 font=label_font, 1236 ) 1237 1238 btx.pos(sx * label_offset, -sy / 2 - sx * 0.66, 0) 1239 tacts.append(btx) 1240 1241 # build above scale 1242 if lut.GetUseAboveRangeColor(): 1243 r, g, b, alfa = lut.GetAboveRangeColor() 1244 arect = shapes.Rectangle( 1245 [-sx * label_offset - sx / 2, sy / 2 + sx * 0.1, 0], 1246 [-sx * label_offset + sx / 2, sy / 2 + sx + sx * 0.1, 0], 1247 c=(r, g, b), 1248 alpha=alfa, 1249 ) 1250 arect.lw(1).lc(c).lighting("off") 1251 scales += [arect] 1252 if above_text is None: 1253 above_text = " >" + str(vmax) 1254 if above_text: 1255 if label_rotation: 1256 atx = shapes.Text3D( 1257 above_text, 1258 (0, 0, 0), 1259 s=lsize, 1260 c=c, 1261 justify="center-top", 1262 italic=italic, 1263 font=label_font, 1264 ) 1265 atx.RotateZ(label_rotation) 1266 else: 1267 atx = shapes.Text3D( 1268 above_text, 1269 (0, 0, 0), 1270 s=lsize, 1271 c=c, 1272 justify="center-left", 1273 italic=italic, 1274 font=label_font, 1275 ) 1276 1277 atx.pos(sx * label_offset, sy / 2 + sx * 0.66, 0) 1278 tacts.append(atx) 1279 1280 # build NaN scale 1281 if lut.GetNanColor() != (0.5, 0.0, 0.0, 1.0): 1282 nanshift = sx * 0.1 1283 if brect: 1284 nanshift += sx 1285 r, g, b, alfa = lut.GetNanColor() 1286 nanrect = shapes.Rectangle( 1287 [-sx * label_offset - sx / 2, -sy / 2 - sx - sx * 0.1 - nanshift, 0], 1288 [-sx * label_offset + sx / 2, -sy / 2 - sx * 0.1 - nanshift, 0], 1289 c=(r, g, b), 1290 alpha=alfa, 1291 ) 1292 nanrect.lw(1).lc(c).lighting("off") 1293 scales += [nanrect] 1294 if label_rotation: 1295 nantx = shapes.Text3D( 1296 nan_text, 1297 (0, 0, 0), 1298 s=lsize, 1299 c=c, 1300 justify="center-left", 1301 italic=italic, 1302 font=label_font, 1303 ) 1304 nantx.RotateZ(label_rotation) 1305 else: 1306 nantx = shapes.Text3D( 1307 nan_text, 1308 (0, 0, 0), 1309 s=lsize, 1310 c=c, 1311 justify="center-left", 1312 italic=italic, 1313 font=label_font, 1314 ) 1315 nantx.pos(sx * label_offset, -sy / 2 - sx * 0.66 - nanshift, 0) 1316 tacts.append(nantx) 1317 1318 if draw_box: 1319 tacts.append(scale.box().lw(1)) 1320 1321 for a in tacts: 1322 a.PickableOff() 1323 1324 mtacts = merge(tacts).lighting("off") 1325 mtacts.PickableOff() 1326 scale.PickableOff() 1327 1328 sact = Assembly(scales + tacts) 1329 sact.SetPosition(pos) 1330 sact.PickableOff() 1331 sact.UseBoundsOff() 1332 sact.name = "ScalarBar3D" 1333 return sact
Create a 3D scalar bar for the specified object.
Input obj
input can be:
- a list of numbers,
- a list of two numbers in the form (min, max),
- a Mesh already containing a set of scalars associated to vertices or cells,
- if None the last object in the list of actors will be used.
Arguments:
- size : (list) (thickness, length) of scalarbar
- title : (str) scalar bar title
- title_xoffset : (float) horizontal space btw title and color scalarbar
- title_yoffset : (float) vertical space offset
- title_size : (float) size of title wrt numeric labels
- title_rotation : (float) title rotation in degrees
- nlabels : (int) number of numeric labels
- label_font : (str) font type for labels
- label_size : (float) label scale factor
- label_offset : (float) space btw numeric labels and scale
- label_rotation : (float) label rotation in degrees
- draw_box : (bool) draw a box around the colorbar
- categories : (list) make a categorical scalarbar, the input list will have the format [value, color, alpha, textlabel]
Examples:
1337class Slider2D(SliderWidget): 1338 """ 1339 Add a slider which can call an external custom function. 1340 """ 1341 def __init__( 1342 self, 1343 sliderfunc, 1344 xmin, 1345 xmax, 1346 value=None, 1347 pos=4, 1348 title="", 1349 font="Calco", 1350 title_size=1, 1351 c="k", 1352 alpha=1, 1353 show_value=True, 1354 delayed=False, 1355 **options, 1356 ): 1357 """ 1358 Add a slider which can call an external custom function. 1359 Set any value as float to increase the number of significant digits above the slider. 1360 1361 Use `play()` to start an animation between the current slider value and the last value. 1362 1363 Arguments: 1364 sliderfunc : (function) 1365 external function to be called by the widget 1366 xmin : (float) 1367 lower value of the slider 1368 xmax : (float) 1369 upper value 1370 value : (float) 1371 current value 1372 pos : (list, str) 1373 position corner number: horizontal [1-5] or vertical [11-15] 1374 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1375 and also by a string descriptor (eg. "bottom-left") 1376 title : (str) 1377 title text 1378 font : (str) 1379 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1380 title_size : (float) 1381 title text scale [1.0] 1382 show_value : (bool) 1383 if True current value is shown 1384 delayed : (bool) 1385 if True the callback is delayed until when the mouse button is released 1386 alpha : (float) 1387 opacity of the scalar bar texts 1388 slider_length : (float) 1389 slider length 1390 slider_width : (float) 1391 slider width 1392 end_cap_length : (float) 1393 length of the end cap 1394 end_cap_width : (float) 1395 width of the end cap 1396 tube_width : (float) 1397 width of the tube 1398 title_height : (float) 1399 height of the title 1400 tformat : (str) 1401 format of the title 1402 1403 Examples: 1404 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1405 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1406 1407 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1408 """ 1409 slider_length = options.pop("slider_length", 0.015) 1410 slider_width = options.pop("slider_width", 0.025) 1411 end_cap_length= options.pop("end_cap_length", 0.0015) 1412 end_cap_width = options.pop("end_cap_width", 0.0125) 1413 tube_width = options.pop("tube_width", 0.0075) 1414 title_height = options.pop("title_height", 0.025) 1415 if options: 1416 vedo.logger.warning(f"in Slider2D unknown option(s): {options}") 1417 1418 c = get_color(c) 1419 1420 if value is None or value < xmin: 1421 value = xmin 1422 1423 slider_rep = vtk.vtkSliderRepresentation2D() 1424 slider_rep.SetMinimumValue(xmin) 1425 slider_rep.SetMaximumValue(xmax) 1426 slider_rep.SetValue(value) 1427 slider_rep.SetSliderLength(slider_length) 1428 slider_rep.SetSliderWidth(slider_width) 1429 slider_rep.SetEndCapLength(end_cap_length) 1430 slider_rep.SetEndCapWidth(end_cap_width) 1431 slider_rep.SetTubeWidth(tube_width) 1432 slider_rep.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay() 1433 slider_rep.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay() 1434 1435 if isinstance(pos, str): 1436 if "top" in pos: 1437 if "left" in pos: 1438 if "vert" in pos: 1439 pos = 11 1440 else: 1441 pos = 1 1442 elif "right" in pos: 1443 if "vert" in pos: 1444 pos = 12 1445 else: 1446 pos = 2 1447 elif "bott" in pos: 1448 if "left" in pos: 1449 if "vert" in pos: 1450 pos = 13 1451 else: 1452 pos = 3 1453 elif "right" in pos: 1454 if "vert" in pos: 1455 if "span" in pos: 1456 pos = 15 1457 else: 1458 pos = 14 1459 else: 1460 pos = 4 1461 elif "span" in pos: 1462 pos = 5 1463 1464 if utils.is_sequence(pos): 1465 slider_rep.GetPoint1Coordinate().SetValue(pos[0][0], pos[0][1]) 1466 slider_rep.GetPoint2Coordinate().SetValue(pos[1][0], pos[1][1]) 1467 elif pos == 1: # top-left horizontal 1468 slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.93) 1469 slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.93) 1470 elif pos == 2: 1471 slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.93) 1472 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.93) 1473 elif pos == 3: 1474 slider_rep.GetPoint1Coordinate().SetValue(0.05, 0.06) 1475 slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.06) 1476 elif pos == 4: # bottom-right 1477 slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06) 1478 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06) 1479 elif pos == 5: # bottom span horizontal 1480 slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.06) 1481 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06) 1482 elif pos == 11: # top-left vertical 1483 slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.54) 1484 slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.9) 1485 elif pos == 12: 1486 slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.54) 1487 slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.9) 1488 elif pos == 13: 1489 slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.1) 1490 slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.54) 1491 elif pos == 14: # bottom-right vertical 1492 slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.1) 1493 slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.54) 1494 elif pos == 15: # right margin vertical 1495 slider_rep.GetPoint1Coordinate().SetValue(0.95, 0.1) 1496 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.9) 1497 else: # bottom-right 1498 slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06) 1499 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06) 1500 1501 if show_value: 1502 if isinstance(xmin, int) and isinstance(xmax, int) and isinstance(value, int): 1503 frm = "%0.0f" 1504 else: 1505 frm = "%0.2f" 1506 1507 frm = options.pop("tformat", frm) 1508 1509 slider_rep.SetLabelFormat(frm) # default is '%0.3g' 1510 slider_rep.GetLabelProperty().SetShadow(0) 1511 slider_rep.GetLabelProperty().SetBold(0) 1512 slider_rep.GetLabelProperty().SetOpacity(alpha) 1513 slider_rep.GetLabelProperty().SetColor(c) 1514 if isinstance(pos, int) and pos > 10: 1515 slider_rep.GetLabelProperty().SetOrientation(90) 1516 else: 1517 slider_rep.ShowSliderLabelOff() 1518 slider_rep.GetTubeProperty().SetColor(c) 1519 slider_rep.GetTubeProperty().SetOpacity(0.75) 1520 slider_rep.GetSliderProperty().SetColor(c) 1521 slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c))) 1522 slider_rep.GetCapProperty().SetColor(c) 1523 1524 slider_rep.SetTitleHeight(title_height * title_size) 1525 slider_rep.GetTitleProperty().SetShadow(0) 1526 slider_rep.GetTitleProperty().SetColor(c) 1527 slider_rep.GetTitleProperty().SetOpacity(alpha) 1528 slider_rep.GetTitleProperty().SetBold(0) 1529 if font.lower() == "courier": 1530 slider_rep.GetTitleProperty().SetFontFamilyToCourier() 1531 elif font.lower() == "times": 1532 slider_rep.GetTitleProperty().SetFontFamilyToTimes() 1533 elif font.lower() == "arial": 1534 slider_rep.GetTitleProperty().SetFontFamilyToArial() 1535 else: 1536 if font == "": 1537 font = utils.get_font_path(settings.default_font) 1538 else: 1539 font = utils.get_font_path(font) 1540 slider_rep.GetTitleProperty().SetFontFamily(vtk.VTK_FONT_FILE) 1541 slider_rep.GetLabelProperty().SetFontFamily(vtk.VTK_FONT_FILE) 1542 slider_rep.GetTitleProperty().SetFontFile(font) 1543 slider_rep.GetLabelProperty().SetFontFile(font) 1544 1545 if title: 1546 slider_rep.SetTitleText(title) 1547 if not utils.is_sequence(pos): 1548 if isinstance(pos, int) and pos > 10: 1549 slider_rep.GetTitleProperty().SetOrientation(90) 1550 else: 1551 if abs(pos[0][0] - pos[1][0]) < 0.1: 1552 slider_rep.GetTitleProperty().SetOrientation(90) 1553 1554 SliderWidget.__init__(self) 1555 1556 self.SetAnimationModeToJump() 1557 self.SetRepresentation(slider_rep) 1558 if delayed: 1559 self.AddObserver("EndInteractionEvent", sliderfunc) 1560 else: 1561 self.AddObserver("InteractionEvent", sliderfunc)
Add a slider which can call an external custom function.
1341 def __init__( 1342 self, 1343 sliderfunc, 1344 xmin, 1345 xmax, 1346 value=None, 1347 pos=4, 1348 title="", 1349 font="Calco", 1350 title_size=1, 1351 c="k", 1352 alpha=1, 1353 show_value=True, 1354 delayed=False, 1355 **options, 1356 ): 1357 """ 1358 Add a slider which can call an external custom function. 1359 Set any value as float to increase the number of significant digits above the slider. 1360 1361 Use `play()` to start an animation between the current slider value and the last value. 1362 1363 Arguments: 1364 sliderfunc : (function) 1365 external function to be called by the widget 1366 xmin : (float) 1367 lower value of the slider 1368 xmax : (float) 1369 upper value 1370 value : (float) 1371 current value 1372 pos : (list, str) 1373 position corner number: horizontal [1-5] or vertical [11-15] 1374 it can also be specified by corners coordinates [(x1,y1), (x2,y2)] 1375 and also by a string descriptor (eg. "bottom-left") 1376 title : (str) 1377 title text 1378 font : (str) 1379 title font face. Check [available fonts here](https://vedo.embl.es/fonts). 1380 title_size : (float) 1381 title text scale [1.0] 1382 show_value : (bool) 1383 if True current value is shown 1384 delayed : (bool) 1385 if True the callback is delayed until when the mouse button is released 1386 alpha : (float) 1387 opacity of the scalar bar texts 1388 slider_length : (float) 1389 slider length 1390 slider_width : (float) 1391 slider width 1392 end_cap_length : (float) 1393 length of the end cap 1394 end_cap_width : (float) 1395 width of the end cap 1396 tube_width : (float) 1397 width of the tube 1398 title_height : (float) 1399 height of the title 1400 tformat : (str) 1401 format of the title 1402 1403 Examples: 1404 - [sliders1.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders1.py) 1405 - [sliders2.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders2.py) 1406 1407 ![](https://user-images.githubusercontent.com/32848391/50738848-be033480-11d8-11e9-9b1a-c13105423a79.jpg) 1408 """ 1409 slider_length = options.pop("slider_length", 0.015) 1410 slider_width = options.pop("slider_width", 0.025) 1411 end_cap_length= options.pop("end_cap_length", 0.0015) 1412 end_cap_width = options.pop("end_cap_width", 0.0125) 1413 tube_width = options.pop("tube_width", 0.0075) 1414 title_height = options.pop("title_height", 0.025) 1415 if options: 1416 vedo.logger.warning(f"in Slider2D unknown option(s): {options}") 1417 1418 c = get_color(c) 1419 1420 if value is None or value < xmin: 1421 value = xmin 1422 1423 slider_rep = vtk.vtkSliderRepresentation2D() 1424 slider_rep.SetMinimumValue(xmin) 1425 slider_rep.SetMaximumValue(xmax) 1426 slider_rep.SetValue(value) 1427 slider_rep.SetSliderLength(slider_length) 1428 slider_rep.SetSliderWidth(slider_width) 1429 slider_rep.SetEndCapLength(end_cap_length) 1430 slider_rep.SetEndCapWidth(end_cap_width) 1431 slider_rep.SetTubeWidth(tube_width) 1432 slider_rep.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay() 1433 slider_rep.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay() 1434 1435 if isinstance(pos, str): 1436 if "top" in pos: 1437 if "left" in pos: 1438 if "vert" in pos: 1439 pos = 11 1440 else: 1441 pos = 1 1442 elif "right" in pos: 1443 if "vert" in pos: 1444 pos = 12 1445 else: 1446 pos = 2 1447 elif "bott" in pos: 1448 if "left" in pos: 1449 if "vert" in pos: 1450 pos = 13 1451 else: 1452 pos = 3 1453 elif "right" in pos: 1454 if "vert" in pos: 1455 if "span" in pos: 1456 pos = 15 1457 else: 1458 pos = 14 1459 else: 1460 pos = 4 1461 elif "span" in pos: 1462 pos = 5 1463 1464 if utils.is_sequence(pos): 1465 slider_rep.GetPoint1Coordinate().SetValue(pos[0][0], pos[0][1]) 1466 slider_rep.GetPoint2Coordinate().SetValue(pos[1][0], pos[1][1]) 1467 elif pos == 1: # top-left horizontal 1468 slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.93) 1469 slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.93) 1470 elif pos == 2: 1471 slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.93) 1472 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.93) 1473 elif pos == 3: 1474 slider_rep.GetPoint1Coordinate().SetValue(0.05, 0.06) 1475 slider_rep.GetPoint2Coordinate().SetValue(0.45, 0.06) 1476 elif pos == 4: # bottom-right 1477 slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06) 1478 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06) 1479 elif pos == 5: # bottom span horizontal 1480 slider_rep.GetPoint1Coordinate().SetValue(0.04, 0.06) 1481 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06) 1482 elif pos == 11: # top-left vertical 1483 slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.54) 1484 slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.9) 1485 elif pos == 12: 1486 slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.54) 1487 slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.9) 1488 elif pos == 13: 1489 slider_rep.GetPoint1Coordinate().SetValue(0.065, 0.1) 1490 slider_rep.GetPoint2Coordinate().SetValue(0.065, 0.54) 1491 elif pos == 14: # bottom-right vertical 1492 slider_rep.GetPoint1Coordinate().SetValue(0.94, 0.1) 1493 slider_rep.GetPoint2Coordinate().SetValue(0.94, 0.54) 1494 elif pos == 15: # right margin vertical 1495 slider_rep.GetPoint1Coordinate().SetValue(0.95, 0.1) 1496 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.9) 1497 else: # bottom-right 1498 slider_rep.GetPoint1Coordinate().SetValue(0.55, 0.06) 1499 slider_rep.GetPoint2Coordinate().SetValue(0.95, 0.06) 1500 1501 if show_value: 1502 if isinstance(xmin, int) and isinstance(xmax, int) and isinstance(value, int): 1503 frm = "%0.0f" 1504 else: 1505 frm = "%0.2f" 1506 1507 frm = options.pop("tformat", frm) 1508 1509 slider_rep.SetLabelFormat(frm) # default is '%0.3g' 1510 slider_rep.GetLabelProperty().SetShadow(0) 1511 slider_rep.GetLabelProperty().SetBold(0) 1512 slider_rep.GetLabelProperty().SetOpacity(alpha) 1513 slider_rep.GetLabelProperty().SetColor(c) 1514 if isinstance(pos, int) and pos > 10: 1515 slider_rep.GetLabelProperty().SetOrientation(90) 1516 else: 1517 slider_rep.ShowSliderLabelOff() 1518 slider_rep.GetTubeProperty().SetColor(c) 1519 slider_rep.GetTubeProperty().SetOpacity(0.75) 1520 slider_rep.GetSliderProperty().SetColor(c) 1521 slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c))) 1522 slider_rep.GetCapProperty().SetColor(c) 1523 1524 slider_rep.SetTitleHeight(title_height * title_size) 1525 slider_rep.GetTitleProperty().SetShadow(0) 1526 slider_rep.GetTitleProperty().SetColor(c) 1527 slider_rep.GetTitleProperty().SetOpacity(alpha) 1528 slider_rep.GetTitleProperty().SetBold(0) 1529 if font.lower() == "courier": 1530 slider_rep.GetTitleProperty().SetFontFamilyToCourier() 1531 elif font.lower() == "times": 1532 slider_rep.GetTitleProperty().SetFontFamilyToTimes() 1533 elif font.lower() == "arial": 1534 slider_rep.GetTitleProperty().SetFontFamilyToArial() 1535 else: 1536 if font == "": 1537 font = utils.get_font_path(settings.default_font) 1538 else: 1539 font = utils.get_font_path(font) 1540 slider_rep.GetTitleProperty().SetFontFamily(vtk.VTK_FONT_FILE) 1541 slider_rep.GetLabelProperty().SetFontFamily(vtk.VTK_FONT_FILE) 1542 slider_rep.GetTitleProperty().SetFontFile(font) 1543 slider_rep.GetLabelProperty().SetFontFile(font) 1544 1545 if title: 1546 slider_rep.SetTitleText(title) 1547 if not utils.is_sequence(pos): 1548 if isinstance(pos, int) and pos > 10: 1549 slider_rep.GetTitleProperty().SetOrientation(90) 1550 else: 1551 if abs(pos[0][0] - pos[1][0]) < 0.1: 1552 slider_rep.GetTitleProperty().SetOrientation(90) 1553 1554 SliderWidget.__init__(self) 1555 1556 self.SetAnimationModeToJump() 1557 self.SetRepresentation(slider_rep) 1558 if delayed: 1559 self.AddObserver("EndInteractionEvent", sliderfunc) 1560 else: 1561 self.AddObserver("InteractionEvent", sliderfunc)
Add a slider which can call an external custom function. Set any value as float to increase the number of significant digits above the slider.
Use play()
to start an animation between the current slider value and the last value.
Arguments:
- sliderfunc : (function) external function to be called by the widget
- xmin : (float) lower value of the slider
- xmax : (float) upper value
- value : (float) current value
- pos : (list, str) position corner number: horizontal [1-5] or vertical [11-15] it can also be specified by corners coordinates [(x1,y1), (x2,y2)] and also by a string descriptor (eg. "bottom-left")
- title : (str) title text
- font : (str) title font face. Check available fonts here.
- title_size : (float) title text scale [1.0]
- show_value : (bool) if True current value is shown
- delayed : (bool) if True the callback is delayed until when the mouse button is released
- alpha : (float) opacity of the scalar bar texts
- slider_length : (float) slider length
- slider_width : (float) slider width
- end_cap_length : (float) length of the end cap
- end_cap_width : (float) width of the end cap
- tube_width : (float) width of the tube
- title_height : (float) height of the title
- tformat : (str) format of the title
Examples:
Inherited Members
1565class Slider3D(SliderWidget): 1566 """ 1567 Add a 3D slider which can call an external custom function. 1568 """ 1569 1570 def __init__( 1571 self, 1572 sliderfunc, 1573 pos1, 1574 pos2, 1575 xmin, 1576 xmax, 1577 value=None, 1578 s=0.03, 1579 t=1, 1580 title="", 1581 rotation=0, 1582 c=None, 1583 show_value=True, 1584 ): 1585 """ 1586 Add a 3D slider which can call an external custom function. 1587 1588 Arguments: 1589 sliderfunc : (function) 1590 external function to be called by the widget 1591 pos1 : (list) 1592 first position 3D coordinates 1593 pos2 : (list) 1594 second position 3D coordinates 1595 xmin : (float) 1596 lower value 1597 xmax : (float) 1598 upper value 1599 value : (float) 1600 initial value 1601 s : (float) 1602 label scaling factor 1603 t : (float) 1604 tube scaling factor 1605 title : (str) 1606 title text 1607 c : (color) 1608 slider color 1609 rotation : (float) 1610 title rotation around slider axis 1611 show_value : (bool) 1612 if True current value is shown on top of the slider 1613 1614 Examples: 1615 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1616 """ 1617 c = get_color(c) 1618 1619 if value is None or value < xmin: 1620 value = xmin 1621 1622 slider_rep = vtk.vtkSliderRepresentation3D() 1623 slider_rep.SetMinimumValue(xmin) 1624 slider_rep.SetMaximumValue(xmax) 1625 slider_rep.SetValue(value) 1626 1627 slider_rep.GetPoint1Coordinate().SetCoordinateSystemToWorld() 1628 slider_rep.GetPoint2Coordinate().SetCoordinateSystemToWorld() 1629 slider_rep.GetPoint1Coordinate().SetValue(pos2) 1630 slider_rep.GetPoint2Coordinate().SetValue(pos1) 1631 1632 # slider_rep.SetPoint1InWorldCoordinates(pos2[0], pos2[1], pos2[2]) 1633 # slider_rep.SetPoint2InWorldCoordinates(pos1[0], pos1[1], pos1[2]) 1634 1635 slider_rep.SetSliderWidth(0.03 * t) 1636 slider_rep.SetTubeWidth(0.01 * t) 1637 slider_rep.SetSliderLength(0.04 * t) 1638 slider_rep.SetSliderShapeToCylinder() 1639 slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c))) 1640 slider_rep.GetSliderProperty().SetColor(np.array(c) / 1.5) 1641 slider_rep.GetCapProperty().SetOpacity(0) 1642 slider_rep.SetRotation(rotation) 1643 1644 if not show_value: 1645 slider_rep.ShowSliderLabelOff() 1646 1647 slider_rep.SetTitleText(title) 1648 slider_rep.SetTitleHeight(s * t) 1649 slider_rep.SetLabelHeight(s * t * 0.85) 1650 1651 slider_rep.GetTubeProperty().SetColor(c) 1652 1653 SliderWidget.__init__(self) 1654 1655 self.SetRepresentation(slider_rep) 1656 self.SetAnimationModeToJump() 1657 self.AddObserver("InteractionEvent", sliderfunc)
Add a 3D slider which can call an external custom function.
1570 def __init__( 1571 self, 1572 sliderfunc, 1573 pos1, 1574 pos2, 1575 xmin, 1576 xmax, 1577 value=None, 1578 s=0.03, 1579 t=1, 1580 title="", 1581 rotation=0, 1582 c=None, 1583 show_value=True, 1584 ): 1585 """ 1586 Add a 3D slider which can call an external custom function. 1587 1588 Arguments: 1589 sliderfunc : (function) 1590 external function to be called by the widget 1591 pos1 : (list) 1592 first position 3D coordinates 1593 pos2 : (list) 1594 second position 3D coordinates 1595 xmin : (float) 1596 lower value 1597 xmax : (float) 1598 upper value 1599 value : (float) 1600 initial value 1601 s : (float) 1602 label scaling factor 1603 t : (float) 1604 tube scaling factor 1605 title : (str) 1606 title text 1607 c : (color) 1608 slider color 1609 rotation : (float) 1610 title rotation around slider axis 1611 show_value : (bool) 1612 if True current value is shown on top of the slider 1613 1614 Examples: 1615 - [sliders3d.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/sliders3d.py) 1616 """ 1617 c = get_color(c) 1618 1619 if value is None or value < xmin: 1620 value = xmin 1621 1622 slider_rep = vtk.vtkSliderRepresentation3D() 1623 slider_rep.SetMinimumValue(xmin) 1624 slider_rep.SetMaximumValue(xmax) 1625 slider_rep.SetValue(value) 1626 1627 slider_rep.GetPoint1Coordinate().SetCoordinateSystemToWorld() 1628 slider_rep.GetPoint2Coordinate().SetCoordinateSystemToWorld() 1629 slider_rep.GetPoint1Coordinate().SetValue(pos2) 1630 slider_rep.GetPoint2Coordinate().SetValue(pos1) 1631 1632 # slider_rep.SetPoint1InWorldCoordinates(pos2[0], pos2[1], pos2[2]) 1633 # slider_rep.SetPoint2InWorldCoordinates(pos1[0], pos1[1], pos1[2]) 1634 1635 slider_rep.SetSliderWidth(0.03 * t) 1636 slider_rep.SetTubeWidth(0.01 * t) 1637 slider_rep.SetSliderLength(0.04 * t) 1638 slider_rep.SetSliderShapeToCylinder() 1639 slider_rep.GetSelectedProperty().SetColor(np.sqrt(np.array(c))) 1640 slider_rep.GetSliderProperty().SetColor(np.array(c) / 1.5) 1641 slider_rep.GetCapProperty().SetOpacity(0) 1642 slider_rep.SetRotation(rotation) 1643 1644 if not show_value: 1645 slider_rep.ShowSliderLabelOff() 1646 1647 slider_rep.SetTitleText(title) 1648 slider_rep.SetTitleHeight(s * t) 1649 slider_rep.SetLabelHeight(s * t * 0.85) 1650 1651 slider_rep.GetTubeProperty().SetColor(c) 1652 1653 SliderWidget.__init__(self) 1654 1655 self.SetRepresentation(slider_rep) 1656 self.SetAnimationModeToJump() 1657 self.AddObserver("InteractionEvent", sliderfunc)
Add a 3D slider which can call an external custom function.
Arguments:
- sliderfunc : (function) external function to be called by the widget
- pos1 : (list) first position 3D coordinates
- pos2 : (list) second position 3D coordinates
- xmin : (float) lower value
- xmax : (float) upper value
- value : (float) initial value
- s : (float) label scaling factor
- t : (float) tube scaling factor
- title : (str) title text
- c : (color) slider color
- rotation : (float) title rotation around slider axis
- show_value : (bool) if True current value is shown on top of the slider
Examples:
Inherited Members
2236class Icon(vtk.vtkOrientationMarkerWidget): 2237 """ 2238 Add an inset icon mesh into the renderer. 2239 """ 2240 2241 def __init__(self, mesh, pos=3, size=0.08): 2242 """ 2243 Arguments: 2244 pos : (list, int) 2245 icon position in the range [1-4] indicating one of the 4 corners, 2246 or it can be a tuple (x,y) as a fraction of the renderer size. 2247 size : (float) 2248 size of the icon space as fraction of the window size. 2249 2250 Examples: 2251 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 2252 """ 2253 vtk.vtkOrientationMarkerWidget.__init__(self) 2254 self.SetOrientationMarker(mesh) 2255 2256 if utils.is_sequence(pos): 2257 self.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 2258 else: 2259 if pos < 2: 2260 self.SetViewport(0, 1 - 2 * size, size * 2, 1) 2261 elif pos == 2: 2262 self.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 2263 elif pos == 3: 2264 self.SetViewport(0, 0, size * 2, size * 2) 2265 elif pos == 4: 2266 self.SetViewport(1 - 2 * size, 0, 1, size * 2)
Add an inset icon mesh into the renderer.
2241 def __init__(self, mesh, pos=3, size=0.08): 2242 """ 2243 Arguments: 2244 pos : (list, int) 2245 icon position in the range [1-4] indicating one of the 4 corners, 2246 or it can be a tuple (x,y) as a fraction of the renderer size. 2247 size : (float) 2248 size of the icon space as fraction of the window size. 2249 2250 Examples: 2251 - [icon.py](https://github.com/marcomusy/vedo/tree/master/examples/other/icon.py) 2252 """ 2253 vtk.vtkOrientationMarkerWidget.__init__(self) 2254 self.SetOrientationMarker(mesh) 2255 2256 if utils.is_sequence(pos): 2257 self.SetViewport(pos[0] - size, pos[1] - size, pos[0] + size, pos[1] + size) 2258 else: 2259 if pos < 2: 2260 self.SetViewport(0, 1 - 2 * size, size * 2, 1) 2261 elif pos == 2: 2262 self.SetViewport(1 - 2 * size, 1 - 2 * size, 1, 1) 2263 elif pos == 3: 2264 self.SetViewport(0, 0, size * 2, size * 2) 2265 elif pos == 4: 2266 self.SetViewport(1 - 2 * size, 0, 1, size * 2)
Arguments:
- pos : (list, int) 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.
- size : (float) size of the icon space as fraction of the window size.
Examples:
213class LegendBox(shapes.TextBase, vtk.vtkLegendBoxActor): 214 """ 215 Create a 2D legend box. 216 """ 217 def __init__( 218 self, 219 entries=(), 220 nmax=12, 221 c=None, 222 font="", 223 width=0.18, 224 height=None, 225 padding=2, 226 bg="k8", 227 alpha=0.25, 228 pos="top-right", 229 markers=None, 230 ): 231 """ 232 Create a 2D legend box for the list of specified objects. 233 234 Arguments: 235 nmax : (int) 236 max number of legend entries 237 c : (color) 238 text color, leave as None to pick the mesh color automatically 239 font : (str) 240 Check [available fonts here](https://vedo.embl.es/fonts) 241 width : (float) 242 width of the box as fraction of the window width 243 height : (float) 244 height of the box as fraction of the window height 245 padding : (int) 246 padding space in units of pixels 247 bg : (color) 248 background color of the box 249 alpha: (float) 250 opacity of the box 251 pos : (str, list) 252 position of the box, can be either a string or a (x,y) screen position in range [0,1] 253 254 Examples: 255 - [legendbox.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/legendbox.py) 256 - [flag_labels1.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels1.py) 257 - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels2.py) 258 259 ![](https://vedo.embl.es/images/other/flag_labels.png) 260 """ 261 vtk.vtkLegendBoxActor.__init__(self) 262 shapes.TextBase.__init__(self) 263 264 self.name = "LegendBox" 265 self.entries = entries[:nmax] 266 self.property = self.GetEntryTextProperty() 267 268 n = 0 269 texts = [] 270 for e in self.entries: 271 ename = e.name 272 if "legend" in e.info.keys(): 273 if not e.info["legend"]: 274 ename = "" 275 else: 276 ename = str(e.info["legend"]) 277 278 if not isinstance(e, vtk.vtkActor): 279 ename = "" 280 if ename: 281 n += 1 282 texts.append(ename) 283 self.SetNumberOfEntries(n) 284 285 if not n: 286 return 287 288 self.ScalarVisibilityOff() 289 self.PickableOff() 290 self.SetPadding(padding) 291 292 self.property.ShadowOff() 293 self.property.BoldOff() 294 295 # self.property.SetJustificationToLeft() # no effect 296 # self.property.SetVerticalJustificationToTop() 297 298 if not font: 299 font = settings.default_font 300 301 self.font(font) 302 303 n = 0 304 for i in range(len(self.entries)): 305 ti = texts[i] 306 if not ti: 307 continue 308 e = entries[i] 309 if c is None: 310 col = e.GetProperty().GetColor() 311 if col == (1, 1, 1): 312 col = (0.2, 0.2, 0.2) 313 else: 314 col = get_color(c) 315 if markers is None: # default 316 poly = e.inputdata() 317 else: 318 marker = markers[i] if utils.is_sequence(markers) else markers 319 if isinstance(marker, vedo.Points): 320 poly = marker.clone(deep=False).normalize().shift(0, 1, 0).polydata() 321 else: # assume string marker 322 poly = vedo.shapes.Marker(marker, s=1).shift(0, 1, 0).polydata() 323 324 self.SetEntry(n, poly, ti, col) 325 n += 1 326 327 self.SetWidth(width) 328 if height is None: 329 self.SetHeight(width / 3.0 * n) 330 else: 331 self.SetHeight(height) 332 333 sx, sy = 1 - self.GetWidth(), 1 - self.GetHeight() 334 if pos == 1 or ("top" in pos and "left" in pos): 335 self.GetPositionCoordinate().SetValue(0, sy) 336 elif pos == 2 or ("top" in pos and "right" in pos): 337 self.GetPositionCoordinate().SetValue(sx, sy) 338 elif pos == 3 or ("bottom" in pos and "left" in pos): 339 self.GetPositionCoordinate().SetValue(0, 0) 340 elif pos == 4 or ("bottom" in pos and "right" in pos): 341 self.GetPositionCoordinate().SetValue(sx, 0) 342 if alpha: 343 self.UseBackgroundOn() 344 self.SetBackgroundColor(get_color(bg)) 345 self.SetBackgroundOpacity(alpha) 346 else: 347 self.UseBackgroundOff() 348 self.LockBorderOn()
Create a 2D legend box.
217 def __init__( 218 self, 219 entries=(), 220 nmax=12, 221 c=None, 222 font="", 223 width=0.18, 224 height=None, 225 padding=2, 226 bg="k8", 227 alpha=0.25, 228 pos="top-right", 229 markers=None, 230 ): 231 """ 232 Create a 2D legend box for the list of specified objects. 233 234 Arguments: 235 nmax : (int) 236 max number of legend entries 237 c : (color) 238 text color, leave as None to pick the mesh color automatically 239 font : (str) 240 Check [available fonts here](https://vedo.embl.es/fonts) 241 width : (float) 242 width of the box as fraction of the window width 243 height : (float) 244 height of the box as fraction of the window height 245 padding : (int) 246 padding space in units of pixels 247 bg : (color) 248 background color of the box 249 alpha: (float) 250 opacity of the box 251 pos : (str, list) 252 position of the box, can be either a string or a (x,y) screen position in range [0,1] 253 254 Examples: 255 - [legendbox.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/legendbox.py) 256 - [flag_labels1.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels1.py) 257 - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/other/flag_labels2.py) 258 259 ![](https://vedo.embl.es/images/other/flag_labels.png) 260 """ 261 vtk.vtkLegendBoxActor.__init__(self) 262 shapes.TextBase.__init__(self) 263 264 self.name = "LegendBox" 265 self.entries = entries[:nmax] 266 self.property = self.GetEntryTextProperty() 267 268 n = 0 269 texts = [] 270 for e in self.entries: 271 ename = e.name 272 if "legend" in e.info.keys(): 273 if not e.info["legend"]: 274 ename = "" 275 else: 276 ename = str(e.info["legend"]) 277 278 if not isinstance(e, vtk.vtkActor): 279 ename = "" 280 if ename: 281 n += 1 282 texts.append(ename) 283 self.SetNumberOfEntries(n) 284 285 if not n: 286 return 287 288 self.ScalarVisibilityOff() 289 self.PickableOff() 290 self.SetPadding(padding) 291 292 self.property.ShadowOff() 293 self.property.BoldOff() 294 295 # self.property.SetJustificationToLeft() # no effect 296 # self.property.SetVerticalJustificationToTop() 297 298 if not font: 299 font = settings.default_font 300 301 self.font(font) 302 303 n = 0 304 for i in range(len(self.entries)): 305 ti = texts[i] 306 if not ti: 307 continue 308 e = entries[i] 309 if c is None: 310 col = e.GetProperty().GetColor() 311 if col == (1, 1, 1): 312 col = (0.2, 0.2, 0.2) 313 else: 314 col = get_color(c) 315 if markers is None: # default 316 poly = e.inputdata() 317 else: 318 marker = markers[i] if utils.is_sequence(markers) else markers 319 if isinstance(marker, vedo.Points): 320 poly = marker.clone(deep=False).normalize().shift(0, 1, 0).polydata() 321 else: # assume string marker 322 poly = vedo.shapes.Marker(marker, s=1).shift(0, 1, 0).polydata() 323 324 self.SetEntry(n, poly, ti, col) 325 n += 1 326 327 self.SetWidth(width) 328 if height is None: 329 self.SetHeight(width / 3.0 * n) 330 else: 331 self.SetHeight(height) 332 333 sx, sy = 1 - self.GetWidth(), 1 - self.GetHeight() 334 if pos == 1 or ("top" in pos and "left" in pos): 335 self.GetPositionCoordinate().SetValue(0, sy) 336 elif pos == 2 or ("top" in pos and "right" in pos): 337 self.GetPositionCoordinate().SetValue(sx, sy) 338 elif pos == 3 or ("bottom" in pos and "left" in pos): 339 self.GetPositionCoordinate().SetValue(0, 0) 340 elif pos == 4 or ("bottom" in pos and "right" in pos): 341 self.GetPositionCoordinate().SetValue(sx, 0) 342 if alpha: 343 self.UseBackgroundOn() 344 self.SetBackgroundColor(get_color(bg)) 345 self.SetBackgroundOpacity(alpha) 346 else: 347 self.UseBackgroundOff() 348 self.LockBorderOn()
Create a 2D legend box for the list of specified objects.
Arguments:
- nmax : (int) max number of legend entries
- c : (color) text color, leave as None to pick the mesh color automatically
- font : (str) Check available fonts here
- width : (float) width of the box as fraction of the window width
- height : (float) height of the box as fraction of the window height
- padding : (int) padding space in units of pixels
- bg : (color) background color of the box
- alpha: (float) opacity of the box
- pos : (str, list) position of the box, can be either a string or a (x,y) screen position in range [0,1]
Examples:
Inherited Members
813def Light(pos, focal_point=(0, 0, 0), angle=180, c=None, intensity=1): 814 """ 815 Generate a source of light placed at `pos` and directed to `focal point`. 816 Returns a `vtkLight` object. 817 818 Arguments: 819 focal_point : (list) 820 focal point, if a `vedo` object is passed then will grab its position. 821 angle : (float) 822 aperture angle of the light source, in degrees 823 c : (color) 824 set the light color 825 intensity : (float) 826 intensity value between 0 and 1. 827 828 Check also: 829 `plotter.Plotter.remove_lights()` 830 831 Examples: 832 - [lights.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/lights.py) 833 834 ![](https://vedo.embl.es/images/basic/lights.png) 835 """ 836 if c is None: 837 try: 838 c = pos.color() 839 except AttributeError: 840 c = "white" 841 842 if isinstance(pos, vedo.Base3DProp): 843 pos = pos.pos() 844 845 if isinstance(focal_point, vedo.Base3DProp): 846 focal_point = focal_point.pos() 847 848 light = vtk.vtkLight() 849 light.SetLightTypeToSceneLight() 850 light.SetPosition(pos) 851 light.SetConeAngle(angle) 852 light.SetFocalPoint(focal_point) 853 light.SetIntensity(intensity) 854 light.SetColor(get_color(c)) 855 return light
Generate a source of light placed at pos
and directed to focal point
.
Returns a vtkLight
object.
Arguments:
- focal_point : (list)
focal point, if a
vedo
object is passed then will grab its position. - angle : (float) aperture angle of the light source, in degrees
- c : (color) set the light color
- intensity : (float) intensity value between 0 and 1.
Check also:
plotter.Plotter.remove_lights()
Examples:
2794def Axes( 2795 obj=None, 2796 xtitle='x', ytitle='y', ztitle='z', 2797 xrange=None, yrange=None, zrange=None, 2798 c=None, 2799 number_of_divisions=None, 2800 digits=None, 2801 limit_ratio=0.04, 2802 title_depth=0, 2803 title_font="", # grab settings.default_font 2804 text_scale=1.0, 2805 x_values_and_labels=None, y_values_and_labels=None, z_values_and_labels=None, 2806 htitle="", 2807 htitle_size=0.03, 2808 htitle_font=None, 2809 htitle_italic=False, 2810 htitle_color=None, 2811 htitle_justify='bottom-center', 2812 htitle_rotation=0, 2813 htitle_offset=(0, 0.01, 0), 2814 xtitle_position=0.95, ytitle_position=0.95, ztitle_position=0.95, 2815 xtitle_offset=0.025, ytitle_offset=0.0275, ztitle_offset=0.02, # can be a list (dx,dy,dz) 2816 xtitle_justify=None, ytitle_justify=None, ztitle_justify=None, 2817 xtitle_rotation=0, ytitle_rotation=0, ztitle_rotation=0, # can be a list (rx,ry,rz) 2818 xtitle_box=False, ytitle_box=False, 2819 xtitle_size=0.025, ytitle_size=0.025, ztitle_size=0.025, 2820 xtitle_color=None, ytitle_color=None, ztitle_color=None, 2821 xtitle_backface_color=None, ytitle_backface_color=None, ztitle_backface_color=None, 2822 xtitle_italic=0, ytitle_italic=0, ztitle_italic=0, 2823 grid_linewidth=1, 2824 xygrid=True, yzgrid=False, zxgrid=False, 2825 xygrid2=False, yzgrid2=False, zxgrid2=False, 2826 xygrid_transparent=False, yzgrid_transparent=False, zxgrid_transparent=False, 2827 xygrid2_transparent=False, yzgrid2_transparent=False, zxgrid2_transparent=False, 2828 xyplane_color=None, yzplane_color=None, zxplane_color=None, 2829 xygrid_color=None, yzgrid_color=None, zxgrid_color=None, 2830 xyalpha=0.075, yzalpha=0.075, zxalpha=0.075, 2831 xyframe_line=None, yzframe_line=None, zxframe_line=None, 2832 xyframe_color=None, yzframe_color=None, zxframe_color=None, 2833 axes_linewidth=1, 2834 xline_color=None, yline_color=None, zline_color=None, 2835 xhighlight_zero=False, yhighlight_zero=False, zhighlight_zero=False, 2836 xhighlight_zero_color='red4', yhighlight_zero_color='green4', zhighlight_zero_color='blue4', 2837 show_ticks=True, 2838 xtick_length=0.015, ytick_length=0.015, ztick_length=0.015, 2839 xtick_thickness=0.0025, ytick_thickness=0.0025, ztick_thickness=0.0025, 2840 xminor_ticks=1, yminor_ticks=1, zminor_ticks=1, 2841 tip_size=None, 2842 label_font="", # grab settings.default_font 2843 xlabel_color=None, ylabel_color=None, zlabel_color=None, 2844 xlabel_size=0.016, ylabel_size=0.016, zlabel_size=0.016, 2845 xlabel_offset=0.8, ylabel_offset=0.8, zlabel_offset=0.8, # each can be a list (dx,dy,dz) 2846 xlabel_justify=None, ylabel_justify=None, zlabel_justify=None, 2847 xlabel_rotation=0, ylabel_rotation=0, zlabel_rotation=0, # each can be a list (rx,ry,rz) 2848 xaxis_rotation=0, yaxis_rotation=0, zaxis_rotation=0, # rotate all elements around axis 2849 xyshift=0, yzshift=0, zxshift=0, 2850 xshift_along_y=0, xshift_along_z=0, 2851 yshift_along_x=0, yshift_along_z=0, 2852 zshift_along_x=0, zshift_along_y=0, 2853 x_use_bounds=True, y_use_bounds=True, z_use_bounds=False, 2854 x_inverted=False, y_inverted=False, z_inverted=False, 2855 use_global=False, 2856 tol=0.001, 2857 **options, 2858 ): 2859 """ 2860 Draw axes for the input object. 2861 Check [available fonts here](https://vedo.embl.es/fonts). 2862 2863 Returns an `vedo.Assembly` object. 2864 2865 Parameters 2866 ---------- 2867 2868 - `xtitle`, ['x'], x-axis title text 2869 - `xrange`, [None], x-axis range in format (xmin, ymin), default is automatic. 2870 - `number_of_divisions`, [None], approximate number of divisions on the longest axis 2871 - `axes_linewidth`, [1], width of the axes lines 2872 - `grid_linewidth`, [1], width of the grid lines 2873 - `title_depth`, [0], extrusion fractional depth of title text 2874 - `x_values_and_labels` [], assign custom tick positions and labels [(pos1, label1), ...] 2875 - `xygrid`, [True], show a gridded wall on plane xy 2876 - `yzgrid`, [True], show a gridded wall on plane yz 2877 - `zxgrid`, [True], show a gridded wall on plane zx 2878 - `zxgrid2`, [False], show zx plane on opposite side of the bounding box 2879 - `xygrid_transparent` [False], make grid plane completely transparent 2880 - `xygrid2_transparent` [False], make grid plane completely transparent on opposite side box 2881 - `xyplane_color`, ['None'], color of the plane 2882 - `xygrid_color`, ['None'], grid line color 2883 - `xyalpha`, [0.15], grid plane opacity 2884 - `xyframe_line`, [0], add a frame for the plane, use value as the thickness 2885 - `xyframe_color`, [None], color for the frame of the plane 2886 - `show_ticks`, [True], show major ticks 2887 - `digits`, [None], use this number of significant digits in scientific notation 2888 - `title_font`, [''], font for axes titles 2889 - `label_font`, [''], font for numeric labels 2890 - `text_scale`, [1.0], global scaling factor for all text elements (titles, labels) 2891 - `htitle`, [''], header title 2892 - `htitle_size`, [0.03], header title size 2893 - `htitle_font`, [None], header font (defaults to `title_font`) 2894 - `htitle_italic`, [True], header font is italic 2895 - `htitle_color`, [None], header title color (defaults to `xtitle_color`) 2896 - `htitle_justify`, ['bottom-center'], origin of the title justification 2897 - `htitle_offset`, [(0,0.01,0)], control offsets of header title in x, y and z 2898 - `xtitle_position`, [0.32], title fractional positions along axis 2899 - `xtitle_offset`, [0.05], title fractional offset distance from axis line, can be a list 2900 - `xtitle_justify`, [None], choose the origin of the bounding box of title 2901 - `xtitle_rotation`, [0], add a rotation of the axis title, can be a list (rx,ry,rz) 2902 - `xtitle_box`, [False], add a box around title text 2903 - `xline_color`, [automatic], color of the x-axis 2904 - `xtitle_color`, [automatic], color of the axis title 2905 - `xtitle_backface_color`, [None], color of axis title on its backface 2906 - `xtitle_size`, [0.025], size of the axis title 2907 - `xtitle_italic`, [0], a bool or float to make the font italic 2908 - `xhighlight_zero`, [True], draw a line highlighting zero position if in range 2909 - `xhighlight_zero_color`, [autom], color of the line highlighting the zero position 2910 - `xtick_length`, [0.005], radius of the major ticks 2911 - `xtick_thickness`, [0.0025], thickness of the major ticks along their axis 2912 - `xminor_ticks`, [1], number of minor ticks between two major ticks 2913 - `xlabel_color`, [automatic], color of numeric labels and ticks 2914 - `xLabelPrecision`, [2], nr. of significative digits to be shown 2915 - `xlabel_size`, [0.015], size of the numeric labels along axis 2916 - `xlabel_rotation`, [0], numeric labels rotation (can be a list of 3 rotations) 2917 - `xlabel_offset`, [0.8], offset of the numeric labels (can be a list of 3 offsets) 2918 - `xlabel_justify`, [None], choose the origin of the bounding box of labels 2919 - `xaxis_rotation`, [0], rotate the X axis elements (ticks and labels) around this same axis 2920 - `xyshift` [0.0], slide the xy-plane along z (the range is [0,1]) 2921 - `xshift_along_y` [0.0], slide x-axis along the y-axis (the range is [0,1]) 2922 - `tip_size`, [0.01], size of the arrow tip 2923 - `limit_ratio`, [0.04], below this ratio don't plot smaller axis 2924 - `x_use_bounds`, [True], keep into account space occupied by labels when setting camera 2925 - `x_inverted`, [False], invert labels order and direction (only visually!) 2926 - `use_global`, [False], try to compute the global bounding box of visible actors 2927 2928 Example: 2929 ```python 2930 from vedo import Axes, Box, show 2931 box = Box(pos=(1,2,3), length=8, width=9, height=7).alpha(0.1) 2932 axs = Axes(box, c='k') # returns an Assembly object 2933 for a in axs.unpack(): 2934 print(a.name) 2935 show(box, axs).close() 2936 ``` 2937 ![](https://vedo.embl.es/images/feaxes1ats/axes1.png) 2938 2939 Examples: 2940 - [custom_axes1.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes1.py) 2941 - [custom_axes2.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes2.py) 2942 - [custom_axes3.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes3.py) 2943 - [custom_axes4.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/custom_axes4.py) 2944 2945 ![](https://vedo.embl.es/images/pyplot/customAxes3.png) 2946 """ 2947 # make sure options are correct and user passed snake_case instead of camelCase 2948 if len(options): 2949 for k in options: 2950 if k.lower() == k: 2951 vedo.logger.warning(f"Unrecognised keyword '{k}' is ignored") 2952 else: 2953 vedo.logger.warning(f"Unrecognised keyword '{k}'. Please use snake_case notation") 2954 2955 if not title_font: 2956 title_font = settings.default_font 2957 if not label_font: 2958 label_font = settings.default_font 2959 2960 if c is None: # automatic black or white 2961 c = (0.1, 0.1, 0.1) 2962 plt = vedo.plotter_instance 2963 if plt and plt.renderer: 2964 bgcol = plt.renderer.GetBackground() 2965 else: 2966 bgcol = (1, 1, 1) 2967 if np.sum(bgcol) < 1.5: 2968 c = (0.9, 0.9, 0.9) 2969 else: 2970 c = get_color(c) 2971 2972 if use_global: 2973 vbb, drange, min_bns, max_bns = compute_visible_bounds() 2974 else: 2975 if obj is not None: 2976 vbb, drange, min_bns, max_bns = compute_visible_bounds(obj) 2977 else: 2978 vbb = np.zeros(6) 2979 drange = np.zeros(3) 2980 if zrange is None: 2981 zrange = (0, 0) 2982 if xrange is None or yrange is None: 2983 vedo.logger.error("in Axes() must specify axes ranges!") 2984 raise RuntimeError() 2985 2986 if xrange is not None: 2987 if xrange[1] < xrange[0]: 2988 x_inverted = True 2989 xrange = [xrange[1], xrange[0]] 2990 vbb[0], vbb[1] = xrange 2991 drange[0] = vbb[1] - vbb[0] 2992 min_bns = vbb 2993 max_bns = vbb 2994 if yrange is not None: 2995 if yrange[1] < yrange[0]: 2996 y_inverted = True 2997 yrange = [yrange[1], yrange[0]] 2998 vbb[2], vbb[3] = yrange 2999 drange[1] = vbb[3] - vbb[2] 3000 min_bns = vbb 3001 max_bns = vbb 3002 if zrange is not None: 3003 if zrange[1] < zrange[0]: 3004 z_inverted = True 3005 zrange = [zrange[1], zrange[0]] 3006 vbb[4], vbb[5] = zrange 3007 drange[2] = vbb[5] - vbb[4] 3008 min_bns = vbb 3009 max_bns = vbb 3010 3011 drangemax = max(drange) 3012 if not drangemax: 3013 return None 3014 3015 if drange[0] / drangemax < limit_ratio: 3016 drange[0] = 0 3017 xtitle = "" 3018 if drange[1] / drangemax < limit_ratio: 3019 drange[1] = 0 3020 ytitle = "" 3021 if drange[2] / drangemax < limit_ratio: 3022 drange[2] = 0 3023 ztitle = "" 3024 3025 x0, x1, y0, y1, z0, z1 = vbb 3026 dx, dy, dz = drange 3027 3028 gscale = np.sqrt(dx * dx + dy * dy + dz * dz) * 0.75 3029 3030 if not xyplane_color: xyplane_color = c 3031 if not yzplane_color: yzplane_color = c 3032 if not zxplane_color: zxplane_color = c 3033 if not xygrid_color: xygrid_color = c 3034 if not yzgrid_color: yzgrid_color = c 3035 if not zxgrid_color: zxgrid_color = c 3036 if not xtitle_color: xtitle_color = c 3037 if not ytitle_color: ytitle_color = c 3038 if not ztitle_color: ztitle_color = c 3039 if not xline_color: xline_color = c 3040 if not yline_color: yline_color = c 3041 if not zline_color: zline_color = c 3042 if not xlabel_color: xlabel_color = xline_color 3043 if not ylabel_color: ylabel_color = yline_color 3044 if not zlabel_color: zlabel_color = zline_color 3045 3046 # vtk version<9 dont like depthpeeling: force switching off grids 3047 if settings.use_depth_peeling and not utils.vtk_version_at_least(9): 3048 xygrid = False 3049 yzgrid = False 3050 zxgrid = False 3051 xygrid2 = False 3052 yzgrid2 = False 3053 zxgrid2 = False 3054 3055 if tip_size is None: 3056 tip_size = 0.005 * gscale 3057 if not ztitle: 3058 tip_size = 0 # switch off in xy 2d 3059 3060 ndiv = 4 3061 if not ztitle or not ytitle or not xtitle: # make more default ticks if 2D 3062 ndiv = 6 3063 if not ztitle: 3064 if xyframe_line is None: 3065 xyframe_line = True 3066 if tip_size is None: 3067 tip_size = False 3068 3069 if utils.is_sequence(number_of_divisions): 3070 rx, ry, rz = number_of_divisions 3071 else: 3072 if not number_of_divisions: 3073 number_of_divisions = ndiv 3074 3075 rx, ry, rz = np.ceil(drange / drangemax * number_of_divisions).astype(int) 3076 3077 if xtitle: 3078 xticks_float, xticks_str = utils.make_ticks(x0, x1, rx, x_values_and_labels, digits) 3079 xticks_float = xticks_float * dx 3080 if x_inverted: 3081 xticks_float = np.flip(-(xticks_float - xticks_float[-1])) 3082 xticks_str = list(reversed(xticks_str)) 3083 xticks_str[-1] = "" 3084 xhighlight_zero = False 3085 if ytitle: 3086 yticks_float, yticks_str = utils.make_ticks(y0, y1, ry, y_values_and_labels, digits) 3087 yticks_float = yticks_float * dy 3088 if y_inverted: 3089 yticks_float = np.flip(-(yticks_float - yticks_float[-1])) 3090 yticks_str = list(reversed(yticks_str)) 3091 yticks_str[-1] = "" 3092 yhighlight_zero = False 3093 if ztitle: 3094 zticks_float, zticks_str = utils.make_ticks(z0, z1, rz, z_values_and_labels, digits) 3095 zticks_float = zticks_float * dz 3096 if z_inverted: 3097 zticks_float = np.flip(-(zticks_float - zticks_float[-1])) 3098 zticks_str = list(reversed(zticks_str)) 3099 zticks_str[-1] = "" 3100 zhighlight_zero = False 3101 3102 ################################################ axes lines 3103 lines = [] 3104 if xtitle: 3105 axlinex = shapes.Line([0,0,0], [dx,0,0], c=xline_color, lw=axes_linewidth) 3106 if xyshift: axlinex.shift(0,0,xyshift*dz) 3107 if zxshift: axlinex.shift(0,zxshift*dy,0) 3108 if xshift_along_y: axlinex.shift(0,xshift_along_y*dy,0) 3109 if xshift_along_z: axlinex.shift(0,0,xshift_along_z*dz) 3110 axlinex.name = 'xAxis' 3111 lines.append(axlinex) 3112 if ytitle: 3113 axliney = shapes.Line([0,0,0], [0,dy,0], c=yline_color, lw=axes_linewidth) 3114 if xyshift: axliney.shift(0,0,xyshift*dz) 3115 if yzshift: axliney.shift(yzshift*dx,0,0) 3116 if yshift_along_x: axliney.shift(yshift_along_x*dx,0,0) 3117 if yshift_along_z: axliney.shift(0,0,yshift_along_z*dz) 3118 axliney.name = 'yAxis' 3119 lines.append(axliney) 3120 if ztitle: 3121 axlinez = shapes.Line([0,0,0], [0,0,dz], c=zline_color, lw=axes_linewidth) 3122 if yzshift: axlinez.shift(yzshift*dx,0,0) 3123 if zxshift: axlinez.shift(0,zxshift*dy,0) 3124 if zshift_along_x: axlinez.shift(zshift_along_x*dx,0,0) 3125 if zshift_along_y: axlinez.shift(0,zshift_along_y*dy,0) 3126 axlinez.name = 'zAxis' 3127 lines.append(axlinez) 3128 3129 ################################################ grid planes 3130 # all shapes have a name to keep track of them in the Assembly 3131 # if user wants to unpack it 3132 grids = [] 3133 if xygrid and xtitle and ytitle: 3134 if not xygrid_transparent: 3135 gxy = shapes.Grid(s=(xticks_float, yticks_float)) 3136 gxy.alpha(xyalpha).c(xyplane_color).lw(0) 3137 if xyshift: gxy.shift(0,0,xyshift*dz) 3138 elif tol: gxy.shift(0,0,-tol*gscale) 3139 gxy.name = "xyGrid" 3140 grids.append(gxy) 3141 if grid_linewidth: 3142 gxy_lines = shapes.Grid(s=(xticks_float, yticks_float)) 3143 gxy_lines.c(xyplane_color).lw(grid_linewidth).alpha(xyalpha) 3144 if xyshift: gxy_lines.shift(0,0,xyshift*dz) 3145 elif tol: gxy_lines.shift(0,0,-tol*gscale) 3146 gxy_lines.name = "xyGridLines" 3147 grids.append(gxy_lines) 3148 3149 if yzgrid and ytitle and ztitle: 3150 if not yzgrid_transparent: 3151 gyz = shapes.Grid(s=(zticks_float, yticks_float)) 3152 gyz.alpha(yzalpha).c(yzplane_color).lw(0).RotateY(-90) 3153 if yzshift: gyz.shift(yzshift*dx,0,0) 3154 elif tol: gyz.shift(-tol*gscale,0,0) 3155 gyz.name = "yzGrid" 3156 grids.append(gyz) 3157 if grid_linewidth: 3158 gyz_lines = shapes.Grid(s=(zticks_float, yticks_float)) 3159 gyz_lines.c(yzplane_color).lw(grid_linewidth).alpha(yzalpha).RotateY(-90) 3160 if yzshift: gyz_lines.shift(yzshift*dx,0,0) 3161 elif tol: gyz_lines.shift(-tol*gscale,0,0) 3162 gyz_lines.name = "yzGridLines" 3163 grids.append(gyz_lines) 3164 3165 if zxgrid and ztitle and xtitle: 3166 if not zxgrid_transparent: 3167 gzx = shapes.Grid(s=(xticks_float, zticks_float)) 3168 gzx.alpha(zxalpha).c(zxplane_color).lw(0).RotateX(90) 3169 if zxshift: gzx.shift(0,zxshift*dy,0) 3170 elif tol: gzx.shift(0,-tol*gscale,0) 3171 gzx.name = "zxGrid" 3172 grids.append(gzx) 3173 if grid_linewidth: 3174 gzx_lines = shapes.Grid(s=(xticks_float, zticks_float)) 3175 gzx_lines.c(zxplane_color).lw(grid_linewidth).alpha(zxalpha).RotateX(90) 3176 if zxshift: gzx_lines.shift(0,zxshift*dy,0) 3177 elif tol: gzx_lines.shift(0,-tol*gscale,0) 3178 gzx_lines.name = "zxGridLines" 3179 grids.append(gzx_lines) 3180 3181 # Grid2 3182 if xygrid2 and xtitle and ytitle: 3183 if not xygrid2_transparent: 3184 gxy2 = shapes.Grid(s=(xticks_float, yticks_float)).z(dz) 3185 gxy2.alpha(xyalpha).c(xyplane_color).lw(0) 3186 if tol: gxy2.shift(0,tol*gscale,0) 3187 gxy2.name = "xyGrid2" 3188 grids.append(gxy2) 3189 if grid_linewidth: 3190 gxy2_lines = shapes.Grid(s=(xticks_float, yticks_float)).z(dz) 3191 gxy2_lines.c(xyplane_color).lw(grid_linewidth).alpha(xyalpha) 3192 if tol: gxy2_lines.shift(0,tol*gscale,0) 3193 gxy2_lines.name = "xygrid2Lines" 3194 grids.append(gxy2_lines) 3195 3196 if yzgrid2 and ytitle and ztitle: 3197 if not yzgrid2_transparent: 3198 gyz2 = shapes.Grid(s=(zticks_float, yticks_float)).x(dx) 3199 gyz2.alpha(yzalpha).c(yzplane_color).lw(0).RotateY(-90) 3200 if tol: gyz2.shift(tol*gscale,0,0) 3201 gyz2.name = "yzGrid2" 3202 grids.append(gyz2) 3203 if grid_linewidth: 3204 gyz2_lines = shapes.Grid(s=(zticks_float, yticks_float)).x(dx) 3205 gyz2_lines.c(yzplane_color).lw(grid_linewidth).alpha(yzalpha).RotateY(-90) 3206 if tol: gyz2_lines.shift(tol*gscale,0,0) 3207 gyz2_lines.name = "yzGrid2Lines" 3208 grids.append(gyz2_lines) 3209 3210 if zxgrid2 and ztitle and xtitle: 3211 if not zxgrid2_transparent: 3212 gzx2 = shapes.Grid(s=(xticks_float, zticks_float)).y(dy) 3213 gzx2.alpha(zxalpha).c(zxplane_color).lw(0).RotateX(90) 3214 if tol: gzx2.shift(0,tol*gscale,0) 3215 gzx2.name = "zxGrid2" 3216 grids.append(gzx2) 3217 if grid_linewidth: 3218 gzx2_lines = shapes.Grid(s=(xticks_float, zticks_float)).y(dy) 3219 gzx2_lines.c(zxplane_color).lw(grid_linewidth).alpha(zxalpha).RotateX(90) 3220 if tol: gzx2_lines.shift(0,tol*gscale,0) 3221 gzx2_lines.name = "zxGrid2Lines" 3222 grids.append(gzx2_lines) 3223 3224 ################################################ frame lines 3225 framelines = [] 3226 if xyframe_line and xtitle and ytitle: 3227 if not xyframe_color: 3228 xyframe_color = xygrid_color 3229 frxy = shapes.Line([[0,dy,0],[dx,dy,0],[dx,0,0],[0,0,0],[0,dy,0]], 3230 c=xyframe_color, lw=xyframe_line) 3231 if xyshift: frxy.shift(0,0,xyshift*dz) 3232 frxy.name = 'xyFrameLine' 3233 framelines.append(frxy) 3234 if yzframe_line and ytitle and ztitle: 3235 if not yzframe_color: 3236 yzframe_color = yzgrid_color 3237 fryz = shapes.Line([[0,0,dz],[0,dy,dz],[0,dy,0],[0,0,0],[0,0,dz]], 3238 c=yzframe_color, lw=yzframe_line) 3239 if yzshift: fryz.shift(yzshift*dx,0,0) 3240 fryz.name = 'yzFrameLine' 3241 framelines.append(fryz) 3242 if zxframe_line and ztitle and xtitle: 3243 if not zxframe_color: 3244 zxframe_color = zxgrid_color 3245 frzx = shapes.Line([[0,0,dz],[dx,0,dz],[dx,0,0],[0,0,0],[0,0,dz]], 3246 c=zxframe_color, lw=zxframe_line) 3247 if zxshift: frzx.shift(0,zxshift*dy,0) 3248 frzx.name = 'zxFrameLine' 3249 framelines.append(frzx) 3250 3251 ################################################ zero lines highlights 3252 highlights = [] 3253 if xygrid and xtitle and ytitle: 3254 if xhighlight_zero and min_bns[0] <= 0 and max_bns[1] > 0: 3255 xhl = -min_bns[0] 3256 hxy = shapes.Line([xhl,0,0], [xhl,dy,0], c=xhighlight_zero_color) 3257 hxy.alpha(np.sqrt(xyalpha)).lw(grid_linewidth*2) 3258 if xyshift: hxy.shift(0,0,xyshift*dz) 3259 hxy.name = "xyHighlightZero" 3260 highlights.append(hxy) 3261 if yhighlight_zero and min_bns[2] <= 0 and max_bns[3] > 0: 3262 yhl = -min_bns[2] 3263 hyx = shapes.Line([0,yhl,0], [dx,yhl,0], c=yhighlight_zero_color) 3264 hyx.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2) 3265 if xyshift: hyx.shift(0,0,xyshift*dz) 3266 hyx.name = "yxHighlightZero" 3267 highlights.append(hyx) 3268 3269 if yzgrid and ytitle and ztitle: 3270 if yhighlight_zero and min_bns[2] <= 0 and max_bns[3] > 0: 3271 yhl = -min_bns[2] 3272 hyz = shapes.Line([0,yhl,0], [0,yhl,dz], c=yhighlight_zero_color) 3273 hyz.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2) 3274 if yzshift: hyz.shift(yzshift*dx,0,0) 3275 hyz.name = "yzHighlightZero" 3276 highlights.append(hyz) 3277 if zhighlight_zero and min_bns[4] <= 0 and max_bns[5] > 0: 3278 zhl = -min_bns[4] 3279 hzy = shapes.Line([0,0,zhl], [0,dy,zhl], c=zhighlight_zero_color) 3280 hzy.alpha(np.sqrt(yzalpha)).lw(grid_linewidth*2) 3281 if yzshift: hzy.shift(yzshift*dx,0,0) 3282 hzy.name = "zyHighlightZero" 3283 highlights.append(hzy) 3284 3285 if zxgrid and ztitle and xtitle: 3286 if zhighlight_zero and min_bns[4] <= 0 and max_bns[5] > 0: 3287 zhl = -min_bns[4] 3288 hzx = shapes.Line([0,0,zhl], [dx,0,zhl], c=zhighlight_zero_color) 3289 hzx.alpha(np.sqrt(zxalpha)).lw(grid_linewidth*2) 3290 if zxshift: hzx.shift(0,zxshift*dy,0) 3291 hzx.name = "zxHighlightZero" 3292 highlights.append(hzx) 3293 if xhighlight_zero and min_bns[0] <= 0 and max_bns[1] > 0: 3294 xhl = -min_bns[0] 3295 hxz = shapes.Line([xhl,0,0], [xhl,0,dz], c=xhighlight_zero_color) 3296 hxz.alpha(np.sqrt(zxalpha)).lw(grid_linewidth*2) 3297 if zxshift: hxz.shift(0,zxshift*dy,0) 3298 hxz.name = "xzHighlightZero" 3299 highlights.append(hxz) 3300 3301 ################################################ arrow cone 3302 cones = [] 3303 3304 if tip_size: 3305 3306 if xtitle: 3307 if x_inverted: 3308 cx = shapes.Cone( 3309 r=tip_size, height=tip_size * 2, axis=(-1, 0, 0), c=xline_color, res=12 3310 ) 3311 else: 3312 cx = shapes.Cone((dx,0,0), r=tip_size, height=tip_size*2, 3313 axis=(1,0,0), c=xline_color, res=12) 3314 if xyshift: cx.shift(0,0,xyshift*dz) 3315 if zxshift: cx.shift(0,zxshift*dy,0) 3316 if xshift_along_y: cx.shift(0,xshift_along_y*dy,0) 3317 if xshift_along_z: cx.shift(0,0,xshift_along_z*dz) 3318 cx.name = "xTipCone" 3319 cones.append(cx) 3320 3321 if ytitle: 3322 if y_inverted: 3323 cy = shapes.Cone(r=tip_size, height=tip_size*2, 3324 axis=(0,-1,0), c=yline_color, res=12) 3325 else: 3326 cy = shapes.Cone((0,dy,0), r=tip_size, height=tip_size*2, 3327 axis=(0,1,0), c=yline_color, res=12) 3328 if xyshift: cy.shift(0,0,xyshift*dz) 3329 if yzshift: cy.shift(yzshift*dx,0,0) 3330 if yshift_along_x: cy.shift(yshift_along_x*dx,0,0) 3331 if yshift_along_z: cy.shift(0,0,yshift_along_z*dz) 3332 cy.name = "yTipCone" 3333 cones.append(cy) 3334 3335 if ztitle: 3336 if z_inverted: 3337 cz = shapes.Cone(r=tip_size, height=tip_size*2, 3338 axis=(0,0,-1), c=zline_color, res=12) 3339 else: 3340 cz = shapes.Cone((0,0,dz), r=tip_size, height=tip_size*2, 3341 axis=(0,0,1), c=zline_color, res=12) 3342 if yzshift: cz.shift(yzshift*dx,0,0) 3343 if zxshift: cz.shift(0,zxshift*dy,0) 3344 if zshift_along_x: cz.shift(zshift_along_x*dx,0,0) 3345 if zshift_along_y: cz.shift(0,zshift_along_y*dy,0) 3346 cz.name = "zTipCone" 3347 cones.append(cz) 3348 3349 ################################################################# MAJOR ticks 3350 majorticks, minorticks = [], [] 3351 xticks, yticks, zticks = [], [], [] 3352 if show_ticks: 3353 if xtitle: 3354 tickThickness = xtick_thickness * gscale / 2 3355 tickLength = xtick_length * gscale / 2 3356 for i in range(1, len(xticks_float) - 1): 3357 v1 = (xticks_float[i] - tickThickness, -tickLength, 0) 3358 v2 = (xticks_float[i] + tickThickness, tickLength, 0) 3359 xticks.append(shapes.Rectangle(v1, v2)) 3360 if len(xticks) > 1: 3361 xmajticks = merge(xticks).c(xlabel_color) 3362 if xaxis_rotation: 3363 xmajticks.RotateX(xaxis_rotation) 3364 if xyshift: xmajticks.shift(0,0,xyshift*dz) 3365 if zxshift: xmajticks.shift(0,zxshift*dy,0) 3366 if xshift_along_y: xmajticks.shift(0,xshift_along_y*dy,0) 3367 if xshift_along_z: xmajticks.shift(0,0,xshift_along_z*dz) 3368 xmajticks.name = "xMajorTicks" 3369 majorticks.append(xmajticks) 3370 if ytitle: 3371 tickThickness = ytick_thickness * gscale / 2 3372 tickLength = ytick_length * gscale / 2 3373 for i in range(1, len(yticks_float) - 1): 3374 v1 = (-tickLength, yticks_float[i] - tickThickness, 0) 3375 v2 = (tickLength, yticks_float[i] + tickThickness, 0) 3376 yticks.append(shapes.Rectangle(v1, v2)) 3377 if len(yticks) > 1: 3378 ymajticks = merge(yticks).c(ylabel_color) 3379 if yaxis_rotation: 3380 ymajticks.RotateY(yaxis_rotation) 3381 if xyshift: ymajticks.shift(0,0,xyshift*dz) 3382 if yzshift: ymajticks.shift(yzshift*dx,0,0) 3383 if yshift_along_x: ymajticks.shift(yshift_along_x*dx,0,0) 3384 if yshift_along_z: ymajticks.shift(0,0,yshift_along_z*dz) 3385 ymajticks.name = "yMajorTicks" 3386 majorticks.append(ymajticks) 3387 if ztitle: 3388 tickThickness = ztick_thickness * gscale / 2 3389 tickLength = ztick_length * gscale / 2.85 3390 for i in range(1, len(zticks_float) - 1): 3391 v1 = (zticks_float[i] - tickThickness, -tickLength, 0) 3392 v2 = (zticks_float[i] + tickThickness, tickLength, 0) 3393 zticks.append(shapes.Rectangle(v1, v2)) 3394 if len(zticks) > 1: 3395 zmajticks = merge(zticks).c(zlabel_color) 3396 zmajticks.RotateZ(-45 + zaxis_rotation) 3397 zmajticks.RotateY(-90) 3398 if yzshift: zmajticks.shift(yzshift*dx,0,0) 3399 if zxshift: zmajticks.shift(0,zxshift*dy,0) 3400 if zshift_along_x: zmajticks.shift(zshift_along_x*dx,0,0) 3401 if zshift_along_y: zmajticks.shift(0,zshift_along_y*dy,0) 3402 zmajticks.name = "zMajorTicks" 3403 majorticks.append(zmajticks) 3404 3405 ############################################################# MINOR ticks 3406 if xtitle and xminor_ticks and len(xticks) > 1: 3407 tickThickness = xtick_thickness * gscale / 4 3408 tickLength = xtick_length * gscale / 4 3409 xminor_ticks += 1 3410 ticks = [] 3411 for i in range(1, len(xticks)): 3412 t0, t1 = xticks[i - 1].pos(), xticks[i].pos() 3413 dt = t1 - t0 3414 for j in range(1, xminor_ticks): 3415 mt = dt * (j / xminor_ticks) + t0 3416 v1 = (mt[0] - tickThickness, -tickLength, 0) 3417 v2 = (mt[0] + tickThickness, tickLength, 0) 3418 ticks.append(shapes.Rectangle(v1, v2)) 3419 3420 # finish off the fist lower range from start to first tick 3421 t0, t1 = xticks[0].pos(), xticks[1].pos() 3422 dt = t1 - t0 3423 for j in range(1, xminor_ticks): 3424 mt = t0 - dt * (j / xminor_ticks) 3425 if mt[0] < 0: 3426 break 3427 v1 = (mt[0] - tickThickness, -tickLength, 0) 3428 v2 = (mt[0] + tickThickness, tickLength, 0) 3429 ticks.append(shapes.Rectangle(v1, v2)) 3430 3431 # finish off the last upper range from last tick to end 3432 t0, t1 = xticks[-2].pos(), xticks[-1].pos() 3433 dt = t1 - t0 3434 for j in range(1, xminor_ticks): 3435 mt = t1 + dt * (j / xminor_ticks) 3436 if mt[0] > dx: 3437 break 3438 v1 = (mt[0] - tickThickness, -tickLength, 0) 3439 v2 = (mt[0] + tickThickness, tickLength, 0) 3440 ticks.append(shapes.Rectangle(v1, v2)) 3441 3442 if ticks: 3443 xminticks = merge(ticks).c(xlabel_color) 3444 if xaxis_rotation: 3445 xminticks.RotateX(xaxis_rotation) 3446 if xyshift: xminticks.shift(0,0,xyshift*dz) 3447 if zxshift: xminticks.shift(0,zxshift*dy,0) 3448 if xshift_along_y: xminticks.shift(0,xshift_along_y*dy,0) 3449 if xshift_along_z: xminticks.shift(0,0,xshift_along_z*dz) 3450 xminticks.name = "xMinorTicks" 3451 minorticks.append(xminticks) 3452 3453 if ytitle and yminor_ticks and len(yticks) > 1: ##### y 3454 tickThickness = ytick_thickness * gscale / 4 3455 tickLength = ytick_length * gscale / 4 3456 yminor_ticks += 1 3457 ticks = [] 3458 for i in range(1, len(yticks)): 3459 t0, t1 = yticks[i - 1].pos(), yticks[i].pos() 3460 dt = t1 - t0 3461 for j in range(1, yminor_ticks): 3462 mt = dt * (j / yminor_ticks) + t0 3463 v1 = (-tickLength, mt[1] - tickThickness, 0) 3464 v2 = (tickLength, mt[1] + tickThickness, 0) 3465 ticks.append(shapes.Rectangle(v1, v2)) 3466 3467 # finish off the fist lower range from start to first tick 3468 t0, t1 = yticks[0].pos(), yticks[1].pos() 3469 dt = t1 - t0 3470 for j in range(1, yminor_ticks): 3471 mt = t0 - dt * (j / yminor_ticks) 3472 if mt[1] < 0: 3473 break 3474 v1 = (-tickLength, mt[1] - tickThickness, 0) 3475 v2 = (tickLength, mt[1] + tickThickness, 0) 3476 ticks.append(shapes.Rectangle(v1, v2)) 3477 3478 # finish off the last upper range from last tick to end 3479 t0, t1 = yticks[-2].pos(), yticks[-1].pos() 3480 dt = t1 - t0 3481 for j in range(1, yminor_ticks): 3482 mt = t1 + dt * (j / yminor_ticks) 3483 if mt[1] > dy: 3484 break 3485 v1 = (-tickLength, mt[1] - tickThickness, 0) 3486 v2 = (tickLength, mt[1] + tickThickness, 0) 3487 ticks.append(shapes.Rectangle(v1, v2)) 3488 3489 if ticks: 3490 yminticks = merge(ticks).c(ylabel_color) 3491 if yaxis_rotation: 3492 yminticks.RotateY(yaxis_rotation) 3493 if xyshift: yminticks.shift(0,0,xyshift*dz) 3494 if yzshift: yminticks.shift(yzshift*dx,0,0) 3495 if yshift_along_x: yminticks.shift(yshift_along_x*dx,0,0) 3496 if yshift_along_z: yminticks.shift(0,0,yshift_along_z*dz) 3497 yminticks.name = "yMinorTicks" 3498 minorticks.append(yminticks) 3499 3500 if ztitle and zminor_ticks and len(zticks) > 1: ##### z 3501 tickThickness = ztick_thickness * gscale / 4 3502 tickLength = ztick_length * gscale / 5 3503 zminor_ticks += 1 3504 ticks = [] 3505 for i in range(1, len(zticks)): 3506 t0, t1 = zticks[i - 1].pos(), zticks[i].pos() 3507 dt = t1 - t0 3508 for j in range(1, zminor_ticks): 3509 mt = dt * (j / zminor_ticks) + t0 3510 v1 = (mt[0] - tickThickness, -tickLength, 0) 3511 v2 = (mt[0] + tickThickness, tickLength, 0) 3512 ticks.append(shapes.Rectangle(v1, v2)) 3513 3514 # finish off the fist lower range from start to first tick 3515 t0, t1 = zticks[0].pos(), zticks[1].pos() 3516 dt = t1 - t0 3517 for j in range(1, zminor_ticks): 3518 mt = t0 - dt * (j / zminor_ticks) 3519 if mt[0] < 0: 3520 break 3521 v1 = (mt[0] - tickThickness, -tickLength, 0) 3522 v2 = (mt[0] + tickThickness, tickLength, 0) 3523 ticks.append(shapes.Rectangle(v1, v2)) 3524 3525 # finish off the last upper range from last tick to end 3526 t0, t1 = zticks[-2].pos(), zticks[-1].pos() 3527 dt = t1 - t0 3528 for j in range(1, zminor_ticks): 3529 mt = t1 + dt * (j / zminor_ticks) 3530 if mt[0] > dz: 3531 break 3532 v1 = (mt[0] - tickThickness, -tickLength, 0) 3533 v2 = (mt[0] + tickThickness, tickLength, 0) 3534 ticks.append(shapes.Rectangle(v1, v2)) 3535 3536 if ticks: 3537 zminticks = merge(ticks).c(zlabel_color) 3538 zminticks.RotateZ(-45 + zaxis_rotation) 3539 zminticks.RotateY(-90) 3540 if yzshift: zminticks.shift(yzshift*dx,0,0) 3541 if zxshift: zminticks.shift(0,zxshift*dy,0) 3542 if zshift_along_x: zminticks.shift(zshift_along_x*dx,0,0) 3543 if zshift_along_y: zminticks.shift(0,zshift_along_y*dy,0) 3544 zminticks.name = "zMinorTicks" 3545 minorticks.append(zminticks) 3546 3547 ################################################ axes NUMERIC text labels 3548 labels = [] 3549 xlab, ylab, zlab = None, None, None 3550 3551 if xlabel_size and xtitle: 3552 3553 xRot, yRot, zRot = 0, 0, 0 3554 if utils.is_sequence(xlabel_rotation): # unpck 3 rotations 3555 zRot, xRot, yRot = xlabel_rotation 3556 else: 3557 zRot = xlabel_rotation 3558 if zRot < 0: # deal with negative angles 3559 zRot += 360 3560 3561 jus = "center-top" 3562 if zRot: 3563 if zRot > 24: jus = "top-right" 3564 if zRot > 67: jus = "center-right" 3565 if zRot > 112: jus = "right-bottom" 3566 if zRot > 157: jus = "center-bottom" 3567 if zRot > 202: jus = "bottom-left" 3568 if zRot > 247: jus = "center-left" 3569 if zRot > 292: jus = "top-left" 3570 if zRot > 337: jus = "top-center" 3571 if xlabel_justify is not None: 3572 jus = xlabel_justify 3573 3574 for i in range(1, len(xticks_str)): 3575 t = xticks_str[i] 3576 if not t: 3577 continue 3578 if utils.is_sequence(xlabel_offset): 3579 xoffs, yoffs, zoffs = xlabel_offset 3580 else: 3581 xoffs, yoffs, zoffs = 0, xlabel_offset, 0 3582 xlab = shapes.Text3D( 3583 t, s=xlabel_size * text_scale * gscale, font=label_font, justify=jus 3584 ) 3585 tb = xlab.ybounds() # must be ybounds: height of char 3586 v = (xticks_float[i], 0, 0) 3587 offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0]) 3588 xlab.pos(v + offs) 3589 if xaxis_rotation: 3590 xlab.rotate_x(xaxis_rotation) 3591 if zRot: xlab.RotateZ(zRot) 3592 if xRot: xlab.RotateX(xRot) 3593 if yRot: xlab.RotateY(yRot) 3594 if xyshift: xlab.shift(0,0,xyshift*dz) 3595 if zxshift: xlab.shift(0,zxshift*dy,0) 3596 if xshift_along_y: xlab.shift(0,xshift_along_y*dy,0) 3597 if xshift_along_z: xlab.shift(0,0,xshift_along_z*dz) 3598 xlab.name = f"xNumericLabel{i}" 3599 xlab.SetUseBounds(x_use_bounds) 3600 labels.append(xlab.c(xlabel_color)) 3601 3602 if ylabel_size and ytitle: 3603 3604 xRot, yRot, zRot = 0, 0, 0 3605 if utils.is_sequence(ylabel_rotation): # unpck 3 rotations 3606 zRot, yRot, xRot = ylabel_rotation 3607 else: 3608 zRot = ylabel_rotation 3609 if zRot < 0: 3610 zRot += 360 # deal with negative angles 3611 3612 jus = "center-right" 3613 if zRot: 3614 if zRot > 24: jus = "bottom-right" 3615 if zRot > 67: jus = "center-bottom" 3616 if zRot > 112: jus = "left-bottom" 3617 if zRot > 157: jus = "center-left" 3618 if zRot > 202: jus = "top-left" 3619 if zRot > 247: jus = "center-top" 3620 if zRot > 292: jus = "top-right" 3621 if zRot > 337: jus = "right-center" 3622 if ylabel_justify is not None: 3623 jus = ylabel_justify 3624 3625 for i in range(1, len(yticks_str)): 3626 t = yticks_str[i] 3627 if not t: 3628 continue 3629 if utils.is_sequence(ylabel_offset): 3630 xoffs, yoffs, zoffs = ylabel_offset 3631 else: 3632 xoffs, yoffs, zoffs = ylabel_offset, 0, 0 3633 ylab = shapes.Text3D( 3634 t, s=ylabel_size * text_scale * gscale, font=label_font, justify=jus 3635 ) 3636 tb = ylab.ybounds() # must be ybounds: height of char 3637 v = (0, yticks_float[i], 0) 3638 offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0]) 3639 ylab.pos(v + offs) 3640 if yaxis_rotation: 3641 ylab.rotate_y(yaxis_rotation) 3642 if zRot: ylab.RotateZ(zRot) 3643 if yRot: ylab.RotateY(yRot) 3644 if xRot: ylab.RotateX(xRot) 3645 if xyshift: ylab.shift(0,0,xyshift*dz) 3646 if yzshift: ylab.shift(yzshift*dx,0,0) 3647 if yshift_along_x: ylab.shift(yshift_along_x*dx,0,0) 3648 if yshift_along_z: ylab.shift(0,0,yshift_along_z*dz) 3649 ylab.name = f"yNumericLabel{i}" 3650 ylab.SetUseBounds(y_use_bounds) 3651 labels.append(ylab.c(ylabel_color)) 3652 3653 if zlabel_size and ztitle: 3654 3655 xRot, yRot, zRot = 0, 0, 0 3656 if utils.is_sequence(zlabel_rotation): # unpck 3 rotations 3657 xRot, yRot, zRot = zlabel_rotation 3658 else: 3659 xRot = zlabel_rotation 3660 if xRot < 0: xRot += 360 # deal with negative angles 3661 3662 jus = "center-right" 3663 if xRot: 3664 if xRot > 24: jus = "bottom-right" 3665 if xRot > 67: jus = "center-bottom" 3666 if xRot > 112: jus = "left-bottom" 3667 if xRot > 157: jus = "center-left" 3668 if xRot > 202: jus = "top-left" 3669 if xRot > 247: jus = "center-top" 3670 if xRot > 292: jus = "top-right" 3671 if xRot > 337: jus = "right-center" 3672 if zlabel_justify is not None: 3673 jus = zlabel_justify 3674 3675 for i in range(1, len(zticks_str)): 3676 t = zticks_str[i] 3677 if not t: 3678 continue 3679 if utils.is_sequence(zlabel_offset): 3680 xoffs, yoffs, zoffs = zlabel_offset 3681 else: 3682 xoffs, yoffs, zoffs = zlabel_offset, zlabel_offset, 0 3683 zlab = shapes.Text3D( 3684 t, s=zlabel_size * text_scale * gscale, font=label_font, justify=jus 3685 ) 3686 tb = zlab.ybounds() # must be ybounds: height of char 3687 v = (0, 0, zticks_float[i]) 3688 offs = -np.array([xoffs, yoffs, zoffs]) * (tb[1] - tb[0]) / 1.5 3689 angle = 90 3690 if dx: 3691 angle = np.arctan2(dy, dx) * 57.3 3692 zlab.RotateZ(angle + yRot) # vtk inverts order of rotations 3693 if xRot: 3694 zlab.RotateY(-xRot) # ..second 3695 zlab.RotateX(90 + zRot) # ..first 3696 zlab.pos(v + offs) 3697 if zaxis_rotation: 3698 zlab.rotate_z(zaxis_rotation) 3699 if yzshift: zlab.shift(yzshift*dx,0,0) 3700 if zxshift: zlab.shift(0,zxshift*dy,0) 3701 if zshift_along_x: zlab.shift(zshift_along_x*dx,0,0) 3702 if zshift_along_y: zlab.shift(0,zshift_along_y*dy,0) 3703 zlab.SetUseBounds(z_use_bounds) 3704 zlab.name = f"zNumericLabel{i}" 3705 labels.append(zlab.c(zlabel_color)) 3706 3707 ################################################ axes titles 3708 titles = [] 3709 3710 if xtitle: 3711 3712 xRot, yRot, zRot = 0, 0, 0 3713 if utils.is_sequence(xtitle_rotation): # unpack 3 rotations 3714 zRot, xRot, yRot = xtitle_rotation 3715 else: 3716 zRot = xtitle_rotation 3717 if zRot < 0: # deal with negative angles 3718 zRot += 360 3719 3720 if utils.is_sequence(xtitle_offset): 3721 xoffs, yoffs, zoffs = xtitle_offset 3722 else: 3723 xoffs, yoffs, zoffs = 0, xtitle_offset, 0 3724 3725 if xtitle_justify is not None: 3726 jus = xtitle_justify 3727 else: 3728 # find best justfication for given rotation(s) 3729 jus = "right-top" 3730 if zRot: 3731 if zRot > 24: jus = "center-right" 3732 if zRot > 67: jus = "right-bottom" 3733 if zRot > 157: jus = "bottom-left" 3734 if zRot > 202: jus = "center-left" 3735 if zRot > 247: jus = "top-left" 3736 if zRot > 337: jus = "top-right" 3737 3738 xt = shapes.Text3D( 3739 xtitle, 3740 s=xtitle_size * text_scale * gscale, 3741 font=title_font, 3742 c=xtitle_color, 3743 justify=jus, 3744 depth=title_depth, 3745 italic=xtitle_italic, 3746 ) 3747 if xtitle_backface_color: 3748 xt.backcolor(xtitle_backface_color) 3749 if zRot: 3750 xt.RotateZ(zRot) 3751 if xRot: 3752 xt.RotateX(xRot) 3753 if yRot: 3754 xt.RotateY(yRot) 3755 shift = 0 3756 if xlab: # xlab is the last created numeric text label.. 3757 lt0, lt1 = xlab.GetBounds()[2:4] 3758 shift = lt1 - lt0 3759 xt.pos( 3760 [(xoffs + xtitle_position) * dx, -(yoffs + xtick_length / 2) * dy - shift, zoffs * dz] 3761 ) 3762 if xaxis_rotation: 3763 xt.rotate_x(xaxis_rotation) 3764 if xyshift: 3765 xt.shift(0, 0, xyshift * dz) 3766 if xshift_along_y: 3767 xt.shift(0, xshift_along_y * dy, 0) 3768 if xshift_along_z: 3769 xt.shift(0, 0, xshift_along_z * dz) 3770 xt.SetUseBounds(x_use_bounds) 3771 if xtitle == " ": 3772 xt.SetUseBounds(False) 3773 xt.name = f"xtitle {xtitle}" 3774 titles.append(xt) 3775 if xtitle_box: 3776 titles.append(xt.box(scale=1.1).use_bounds(x_use_bounds)) 3777 3778 if ytitle: 3779 3780 xRot, yRot, zRot = 0, 0, 0 3781 if utils.is_sequence(ytitle_rotation): # unpck 3 rotations 3782 zRot, yRot, xRot = ytitle_rotation 3783 else: 3784 zRot = ytitle_rotation 3785 if len(ytitle) > 3: 3786 zRot += 90 3787 ytitle_position *= 0.975 3788 if zRot < 0: 3789 zRot += 360 # deal with negative angles 3790 3791 if utils.is_sequence(ytitle_offset): 3792 xoffs, yoffs, zoffs = ytitle_offset 3793 else: 3794 xoffs, yoffs, zoffs = ytitle_offset, 0, 0 3795 3796 if ytitle_justify is not None: 3797 jus = ytitle_justify 3798 else: 3799 jus = "center-right" 3800 if zRot: 3801 if zRot > 24: jus = "bottom-right" 3802 if zRot > 112: jus = "left-bottom" 3803 if zRot > 157: jus = "center-left" 3804 if zRot > 202: jus = "top-left" 3805 if zRot > 292: jus = "top-right" 3806 if zRot > 337: jus = "right-center" 3807 3808 yt = shapes.Text3D( 3809 ytitle, 3810 s=ytitle_size * text_scale * gscale, 3811 font=title_font, 3812 c=ytitle_color, 3813 justify=jus, 3814 depth=title_depth, 3815 italic=ytitle_italic, 3816 ) 3817 if ytitle_backface_color: 3818 yt.backcolor(ytitle_backface_color) 3819 3820 if zRot: yt.RotateZ(zRot) 3821 if yRot: yt.RotateY(yRot) 3822 if xRot: yt.RotateX(xRot) 3823 3824 shift = 0 3825 if ylab: # this is the last created num label.. 3826 lt0, lt1 = ylab.GetBounds()[0:2] 3827 shift = lt1 - lt0 3828 3829 yt.pos(-(xoffs + ytick_length / 2) * dx - shift, (yoffs + ytitle_position) * dy, zoffs * dz) 3830 if yaxis_rotation: 3831 yt.rotate_y(yaxis_rotation) 3832 if xyshift: yt.shift(0, 0, xyshift*dz) 3833 if yshift_along_x: yt.shift(yshift_along_x*dx, 0, 0) 3834 if yshift_along_z: yt.shift(0, 0, yshift_along_z*dz) 3835 yt.SetUseBounds(y_use_bounds) 3836 if ytitle == " ": 3837 yt.SetUseBounds(False) 3838 yt.name = f"ytitle {ytitle}" 3839 titles.append(yt) 3840 if ytitle_box: 3841 titles.append(yt.box(scale=1.1).use_bounds(y_use_bounds)) 3842 3843 if ztitle: 3844 3845 xRot, yRot, zRot = 0, 0, 0 3846 if utils.is_sequence(ztitle_rotation): # unpck 3 rotations 3847 xRot, yRot, zRot = ztitle_rotation 3848 else: 3849 xRot = ztitle_rotation 3850 if len(ztitle) > 3: 3851 xRot += 90 3852 ztitle_position *= 0.975 3853 if xRot < 0: 3854 xRot += 360 # deal with negative angles 3855 3856 if ztitle_justify is not None: 3857 jus = ztitle_justify 3858 else: 3859 jus = "center-right" 3860 if xRot: 3861 if xRot > 24: jus = "bottom-right" 3862 if xRot > 112: jus = "left-bottom" 3863 if xRot > 157: jus = "center-left" 3864 if xRot > 202: jus = "top-left" 3865 if xRot > 292: jus = "top-right" 3866 if xRot > 337: jus = "right-center" 3867 3868 zt = shapes.Text3D( 3869 ztitle, 3870 s=ztitle_size * text_scale * gscale, 3871 font=title_font, 3872 c=ztitle_color, 3873 justify=jus, 3874 depth=title_depth, 3875 italic=ztitle_italic, 3876 ) 3877 if ztitle_backface_color: 3878 zt.backcolor(ztitle_backface_color) 3879 3880 angle = 90 3881 if dx: 3882 angle = np.arctan2(dy, dx) * 57.3 3883 zt.RotateZ(angle + yRot) # vtk inverts order of rotations 3884 if xRot: 3885 zt.RotateY(-xRot) # ..second 3886 zt.RotateX(90 + zRot) # ..first 3887 3888 shift = 0 3889 if zlab: # this is the last created one.. 3890 lt0, lt1 = zlab.GetBounds()[0:2] 3891 shift = lt1 - lt0 3892 zt.pos( 3893 -(ztitle_offset + ztick_length / 5) * dx - shift, 3894 -(ztitle_offset + ztick_length / 5) * dy - shift, 3895 ztitle_position * dz, 3896 ) 3897 if zaxis_rotation: 3898 zt.rotate_z(zaxis_rotation) 3899 if zxshift: zt.shift(0,zxshift*dy,0) 3900 if zshift_along_x: zt.shift(zshift_along_x*dx,0,0) 3901 if zshift_along_y: zt.shift(0,zshift_along_y*dy,0) 3902 zt.SetUseBounds(z_use_bounds) 3903 if ztitle == " ": 3904 zt.SetUseBounds(False) 3905 zt.name = f"ztitle {ztitle}" 3906 titles.append(zt) 3907 3908 ################################################### header title 3909 if htitle: 3910 if htitle_font is None: 3911 htitle_font = title_font 3912 if htitle_color is None: 3913 htitle_color = xtitle_color 3914 htit = shapes.Text3D( 3915 htitle, 3916 s=htitle_size * gscale * text_scale, 3917 font=htitle_font, 3918 c=htitle_color, 3919 justify=htitle_justify, 3920 depth=title_depth, 3921 italic=htitle_italic, 3922 ) 3923 if htitle_rotation: 3924 htit.RotateX(htitle_rotation) 3925 wpos = [(0.5 + htitle_offset[0]) * dx, (1 + htitle_offset[1]) * dy, htitle_offset[2] * dz] 3926 htit.pos(wpos) 3927 if xyshift: 3928 htit.shift(0, 0, xyshift * dz) 3929 htit.name = f"htitle {htitle}" 3930 titles.append(htit) 3931 3932 ###### 3933 acts = titles + lines + labels + grids + framelines 3934 acts += highlights + majorticks + minorticks + cones 3935 orig = (min_bns[0], min_bns[2], min_bns[4]) 3936 for a in acts: 3937 a.PickableOff() 3938 a.AddPosition(orig) 3939 a.GetProperty().LightingOff() 3940 asse = Assembly(acts) 3941 asse.SetOrigin(orig) 3942 asse.PickableOff() 3943 asse.name = "Axes" 3944 return asse
Draw axes for the input object. Check available fonts here.
Returns an vedo.Assembly
object.
Parameters
xtitle
, ['x'], x-axis title textxrange
, [None], x-axis range in format (xmin, ymin), default is automatic.number_of_divisions
, [None], approximate number of divisions on the longest axisaxes_linewidth
, [1], width of the axes linesgrid_linewidth
, [1], width of the grid linestitle_depth
, [0], extrusion fractional depth of title textx_values_and_labels
[], assign custom tick positions and labels [(pos1, label1), ...]xygrid
, [True], show a gridded wall on plane xyyzgrid
, [True], show a gridded wall on plane yzzxgrid
, [True], show a gridded wall on plane zxzxgrid2
, [False], show zx plane on opposite side of the bounding boxxygrid_transparent
[False], make grid plane completely transparentxygrid2_transparent
[False], make grid plane completely transparent on opposite side boxxyplane_color
, ['None'], color of the planexygrid_color
, ['None'], grid line colorxyalpha
, [0.15], grid plane opacityxyframe_line
, [0], add a frame for the plane, use value as the thicknessxyframe_color
, [None], color for the frame of the planeshow_ticks
, [True], show major ticksdigits
, [None], use this number of significant digits in scientific notationtitle_font
, [''], font for axes titleslabel_font
, [''], font for numeric labelstext_scale
, [1.0], global scaling factor for all text elements (titles, labels)htitle
, [''], header titlehtitle_size
, [0.03], header title sizehtitle_font
, [None], header font (defaults totitle_font
)htitle_italic
, [True], header font is italichtitle_color
, [None], header title color (defaults toxtitle_color
)htitle_justify
, ['bottom-center'], origin of the title justificationhtitle_offset
, [(0,0.01,0)], control offsets of header title in x, y and zxtitle_position
, [0.32], title fractional positions along axisxtitle_offset
, [0.05], title fractional offset distance from axis line, can be a listxtitle_justify
, [None], choose the origin of the bounding box of titlextitle_rotation
, [0], add a rotation of the axis title, can be a list (rx,ry,rz)xtitle_box
, [False], add a box around title textxline_color
, [automatic], color of the x-axisxtitle_color
, [automatic], color of the axis titlextitle_backface_color
, [None], color of axis title on its backfacextitle_size
, [0.025], size of the axis titlextitle_italic
, [0], a bool or float to make the font italicxhighlight_zero
, [True], draw a line highlighting zero position if in rangexhighlight_zero_color
, [autom], color of the line highlighting the zero positionxtick_length
, [0.005], radius of the major ticksxtick_thickness
, [0.0025], thickness of the major ticks along their axisxminor_ticks
, [1], number of minor ticks between two major ticksxlabel_color
, [automatic], color of numeric labels and ticksxLabelPrecision
, [2], nr. of significative digits to be shownxlabel_size
, [0.015], size of the numeric labels along axisxlabel_rotation
, [0], numeric labels rotation (can be a list of 3 rotations)xlabel_offset
, [0.8], offset of the numeric labels (can be a list of 3 offsets)xlabel_justify
, [None], choose the origin of the bounding box of labelsxaxis_rotation
, [0], rotate the X axis elements (ticks and labels) around this same axisxyshift
[0.0], slide the xy-plane along z (the range is [0,1])xshift_along_y
[0.0], slide x-axis along the y-axis (the range is [0,1])tip_size
, [0.01], size of the arrow tiplimit_ratio
, [0.04], below this ratio don't plot smaller axisx_use_bounds
, [True], keep into account space occupied by labels when setting camerax_inverted
, [False], invert labels order and direction (only visually!)use_global
, [False], try to compute the global bounding box of visible actors
Example:
from vedo import Axes, Box, show box = Box(pos=(1,2,3), length=8, width=9, height=7).alpha(0.1) axs = Axes(box, c='k') # returns an Assembly object for a in axs.unpack(): print(a.name) show(box, axs).close()
Examples:
2081class RendererFrame(vtk.vtkActor2D): 2082 """ 2083 Add a line around the renderer subwindow. 2084 """ 2085 2086 def __init__(self, c="k", alpha=None, lw=None, padding=None): 2087 """ 2088 Add a line around the renderer subwindow. 2089 2090 Arguments: 2091 c : (color) 2092 color of the line. 2093 alpha : (float) 2094 opacity. 2095 lw : (int) 2096 line width in pixels. 2097 padding : (int) 2098 padding in pixel units. 2099 """ 2100 2101 if lw is None: 2102 lw = settings.renderer_frame_width 2103 if lw == 0: 2104 return None 2105 2106 if alpha is None: 2107 alpha = settings.renderer_frame_alpha 2108 2109 if padding is None: 2110 padding = settings.renderer_frame_padding 2111 2112 c = get_color(c) 2113 2114 ppoints = vtk.vtkPoints() # Generate the polyline 2115 xy = 1 - padding 2116 psqr = [[padding, padding], [padding, xy], [xy, xy], [xy, padding], [padding, padding]] 2117 for i, pt in enumerate(psqr): 2118 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2119 lines = vtk.vtkCellArray() 2120 lines.InsertNextCell(len(psqr)) 2121 for i in range(len(psqr)): 2122 lines.InsertCellPoint(i) 2123 pd = vtk.vtkPolyData() 2124 pd.SetPoints(ppoints) 2125 pd.SetLines(lines) 2126 2127 mapper = vtk.vtkPolyDataMapper2D() 2128 mapper.SetInputData(pd) 2129 cs = vtk.vtkCoordinate() 2130 cs.SetCoordinateSystemToNormalizedViewport() 2131 mapper.SetTransformCoordinate(cs) 2132 2133 vtk.vtkActor2D.__init__(self) 2134 2135 self.GetPositionCoordinate().SetValue(0, 0) 2136 self.GetPosition2Coordinate().SetValue(1, 1) 2137 self.SetMapper(mapper) 2138 self.GetProperty().SetColor(c) 2139 self.GetProperty().SetOpacity(alpha) 2140 self.GetProperty().SetLineWidth(lw)
Add a line around the renderer subwindow.
2086 def __init__(self, c="k", alpha=None, lw=None, padding=None): 2087 """ 2088 Add a line around the renderer subwindow. 2089 2090 Arguments: 2091 c : (color) 2092 color of the line. 2093 alpha : (float) 2094 opacity. 2095 lw : (int) 2096 line width in pixels. 2097 padding : (int) 2098 padding in pixel units. 2099 """ 2100 2101 if lw is None: 2102 lw = settings.renderer_frame_width 2103 if lw == 0: 2104 return None 2105 2106 if alpha is None: 2107 alpha = settings.renderer_frame_alpha 2108 2109 if padding is None: 2110 padding = settings.renderer_frame_padding 2111 2112 c = get_color(c) 2113 2114 ppoints = vtk.vtkPoints() # Generate the polyline 2115 xy = 1 - padding 2116 psqr = [[padding, padding], [padding, xy], [xy, xy], [xy, padding], [padding, padding]] 2117 for i, pt in enumerate(psqr): 2118 ppoints.InsertPoint(i, pt[0], pt[1], 0) 2119 lines = vtk.vtkCellArray() 2120 lines.InsertNextCell(len(psqr)) 2121 for i in range(len(psqr)): 2122 lines.InsertCellPoint(i) 2123 pd = vtk.vtkPolyData() 2124 pd.SetPoints(ppoints) 2125 pd.SetLines(lines) 2126 2127 mapper = vtk.vtkPolyDataMapper2D() 2128 mapper.SetInputData(pd) 2129 cs = vtk.vtkCoordinate() 2130 cs.SetCoordinateSystemToNormalizedViewport() 2131 mapper.SetTransformCoordinate(cs) 2132 2133 vtk.vtkActor2D.__init__(self) 2134 2135 self.GetPositionCoordinate().SetValue(0, 0) 2136 self.GetPosition2Coordinate().SetValue(1, 1) 2137 self.SetMapper(mapper) 2138 self.GetProperty().SetColor(c) 2139 self.GetProperty().SetOpacity(alpha) 2140 self.GetProperty().SetLineWidth(lw)
Add a line around the renderer subwindow.
Arguments:
- c : (color) color of the line.
- alpha : (float) opacity.
- lw : (int) line width in pixels.
- padding : (int) padding in pixel units.
2304def Ruler( 2305 p1, 2306 p2, 2307 units_scale=1, 2308 label="", 2309 s=None, 2310 font=None, 2311 italic=0, 2312 prefix="", 2313 units="", # eg.'μm' 2314 c=(0.2, 0.1, 0.1), 2315 alpha=1, 2316 lw=1, 2317 precision=3, 2318 label_rotation=0, 2319 axis_rotation=0, 2320 tick_angle=90, 2321): 2322 """ 2323 Build a 3D ruler to indicate the distance of two points p1 and p2. 2324 2325 Arguments: 2326 label : (str) 2327 alternative fixed label to be shown 2328 units_scale : (float) 2329 factor to scale units (e.g. μm to mm) 2330 s : (float) 2331 size of the label 2332 font : (str) 2333 font face. Check [available fonts here](https://vedo.embl.es/fonts). 2334 italic : (float) 2335 italicness of the font in the range [0,1] 2336 units : (str) 2337 string to be appended to the numeric value 2338 lw : (int) 2339 line width in pixel units 2340 precision : (int) 2341 nr of significant digits to be shown 2342 label_rotation : (float) 2343 initial rotation of the label around the z-axis 2344 axis_rotation : (float) 2345 initial rotation of the line around the main axis 2346 tick_angle : (float) 2347 initial rotation of the line around the main axis 2348 2349 Examples: 2350 - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py) 2351 2352 ![](https://vedo.embl.es/images/pyplot/goniometer.png) 2353 """ 2354 if units_scale != 1.0 and units == "": 2355 raise ValueError( 2356 "When setting 'units_scale' to a value other than 1, " 2357 + "a 'units' arguments must be specified." 2358 ) 2359 2360 if isinstance(p1, Points): 2361 p1 = p1.GetPosition() 2362 if isinstance(p2, Points): 2363 p2 = p2.GetPosition() 2364 2365 if len(p1) == 2: 2366 p1 = [p1[0], p1[1], 0.0] 2367 if len(p2) == 2: 2368 p2 = [p2[0], p2[1], 0.0] 2369 2370 p1, p2 = np.array(p1), np.array(p2) 2371 q1, q2 = [0, 0, 0], [utils.mag(p2 - p1), 0, 0] 2372 q1, q2 = np.array(q1), np.array(q2) 2373 v = q2 - q1 2374 d = utils.mag(v) * units_scale 2375 2376 if s is None: 2377 s = d * 0.02 * (1 / units_scale) 2378 2379 if not label: 2380 label = str(d) 2381 if precision: 2382 label = utils.precision(d, precision) 2383 if prefix: 2384 label = prefix + "~" + label 2385 if units: 2386 label += "~" + units 2387 2388 lb = shapes.Text3D(label, pos=(q1 + q2) / 2, s=s, font=font, italic=italic, justify="center") 2389 if label_rotation: 2390 lb.RotateZ(label_rotation) 2391 2392 x0, x1 = lb.xbounds() 2393 gap = [(x1 - x0) / 2, 0, 0] 2394 pc1 = (v / 2 - gap) * 0.9 + q1 2395 pc2 = q2 - (v / 2 - gap) * 0.9 2396 2397 lc1 = shapes.Line(q1 - v / 50, pc1) 2398 lc2 = shapes.Line(q2 + v / 50, pc2) 2399 2400 zs = np.array([0, d / 50 * (1 / units_scale), 0]) 2401 ml1 = shapes.Line(-zs, zs).pos(q1) 2402 ml2 = shapes.Line(-zs, zs).pos(q2) 2403 ml1.RotateZ(tick_angle - 90) 2404 ml2.RotateZ(tick_angle - 90) 2405 2406 c1 = shapes.Circle(q1, r=d / 180 * (1 / units_scale), res=20) 2407 c2 = shapes.Circle(q2, r=d / 180 * (1 / units_scale), res=20) 2408 2409 acts = [lb, lc1, lc2, c1, c2, ml1, ml2] 2410 macts = merge(acts).pos(p1).c(c).alpha(alpha) 2411 macts.GetProperty().LightingOff() 2412 macts.GetProperty().SetLineWidth(lw) 2413 macts.UseBoundsOff() 2414 macts.base = q1 2415 macts.top = q2 2416 macts.orientation(p2 - p1, rotation=axis_rotation).bc("t").pickable(False) 2417 return macts
Build a 3D ruler to indicate the distance of two points p1 and p2.
Arguments:
- label : (str) alternative fixed label to be shown
- units_scale : (float) factor to scale units (e.g. μm to mm)
- s : (float) size of the label
- font : (str) font face. Check available fonts here.
- italic : (float) italicness of the font in the range [0,1]
- units : (str) string to be appended to the numeric value
- lw : (int) line width in pixel units
- precision : (int) nr of significant digits to be shown
- label_rotation : (float) initial rotation of the label around the z-axis
- axis_rotation : (float) initial rotation of the line around the main axis
- tick_angle : (float) initial rotation of the line around the main axis
Examples:
2420def RulerAxes( 2421 inputobj, 2422 xtitle="", 2423 ytitle="", 2424 ztitle="", 2425 xlabel="", 2426 ylabel="", 2427 zlabel="", 2428 xpadding=0.05, 2429 ypadding=0.04, 2430 zpadding=0, 2431 font="Normografo", 2432 s=None, 2433 italic=0, 2434 units="", 2435 c=(0.2, 0, 0), 2436 alpha=1, 2437 lw=1, 2438 precision=3, 2439 label_rotation=0, 2440 xaxis_rotation=0, 2441 yaxis_rotation=0, 2442 zaxis_rotation=0, 2443 xycross=True, 2444): 2445 """ 2446 A 3D ruler axes to indicate the sizes of the input scene or object. 2447 2448 Arguments: 2449 xtitle : (str) 2450 name of the axis or title 2451 xlabel : (str) 2452 alternative fixed label to be shown instead of the distance 2453 s : (float) 2454 size of the label 2455 font : (str) 2456 font face. Check [available fonts here](https://vedo.embl.es/fonts). 2457 italic : (float) 2458 italicness of the font in the range [0,1] 2459 units : (str) 2460 string to be appended to the numeric value 2461 lw : (int) 2462 line width in pixel units 2463 precision : (int) 2464 nr of significant digits to be shown 2465 label_rotation : (float) 2466 initial rotation of the label around the z-axis 2467 [x,y,z]axis_rotation : (float) 2468 initial rotation of the line around the main axis in degrees 2469 xycross : (bool) 2470 show two back crossing lines in the xy plane 2471 2472 Examples: 2473 - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py) 2474 """ 2475 if utils.is_sequence(inputobj): 2476 x0, x1, y0, y1, z0, z1 = inputobj 2477 else: 2478 x0, x1, y0, y1, z0, z1 = inputobj.bounds() 2479 dx, dy, dz = (y1 - y0) * xpadding, (x1 - x0) * ypadding, (y1 - y0) * zpadding 2480 d = np.sqrt((y1 - y0) ** 2 + (x1 - x0) ** 2 + (z1 - z0) ** 2) 2481 2482 if not d: 2483 return None 2484 2485 if s is None: 2486 s = d / 75 2487 2488 acts, rx, ry = [], None, None 2489 if xtitle is not None and (x1 - x0) / d > 0.1: 2490 rx = Ruler( 2491 [x0, y0 - dx, z0], 2492 [x1, y0 - dx, z0], 2493 s=s, 2494 font=font, 2495 precision=precision, 2496 label_rotation=label_rotation, 2497 axis_rotation=xaxis_rotation, 2498 lw=lw, 2499 italic=italic, 2500 prefix=xtitle, 2501 label=xlabel, 2502 units=units, 2503 ) 2504 acts.append(rx) 2505 if ytitle is not None and (y1 - y0) / d > 0.1: 2506 ry = Ruler( 2507 [x1 + dy, y0, z0], 2508 [x1 + dy, y1, z0], 2509 s=s, 2510 font=font, 2511 precision=precision, 2512 label_rotation=label_rotation, 2513 axis_rotation=yaxis_rotation, 2514 lw=lw, 2515 italic=italic, 2516 prefix=ytitle, 2517 label=ylabel, 2518 units=units, 2519 ) 2520 acts.append(ry) 2521 if ztitle is not None and (z1 - z0) / d > 0.1: 2522 rz = Ruler( 2523 [x0 - dy, y0 + dz, z0], 2524 [x0 - dy, y0 + dz, z1], 2525 s=s, 2526 font=font, 2527 precision=precision, 2528 label_rotation=label_rotation, 2529 axis_rotation=zaxis_rotation + 90, 2530 lw=lw, 2531 italic=italic, 2532 prefix=ztitle, 2533 label=zlabel, 2534 units=units, 2535 ) 2536 acts.append(rz) 2537 2538 if xycross and rx and ry: 2539 lx = shapes.Line([x0, y0, z0], [x0, y1 + dx, z0]) 2540 ly = shapes.Line([x0 - dy, y1, z0], [x1, y1, z0]) 2541 d = min((x1 - x0), (y1 - y0)) / 200 2542 cxy = shapes.Circle([x0, y1, z0], r=d, res=15) 2543 acts.extend([lx, ly, cxy]) 2544 2545 macts = merge(acts) 2546 if not macts: 2547 return None 2548 macts.c(c).alpha(alpha).bc("t") 2549 macts.UseBoundsOff() 2550 macts.PickableOff() 2551 return macts
A 3D ruler axes to indicate the sizes of the input scene or object.
Arguments:
- xtitle : (str) name of the axis or title
- xlabel : (str) alternative fixed label to be shown instead of the distance
- s : (float) size of the label
- font : (str) font face. Check available fonts here.
- italic : (float) italicness of the font in the range [0,1]
- units : (str) string to be appended to the numeric value
- lw : (int) line width in pixel units
- precision : (int) nr of significant digits to be shown
- label_rotation : (float) initial rotation of the label around the z-axis
- [x,y,z]axis_rotation : (float) initial rotation of the line around the main axis in degrees
- xycross : (bool) show two back crossing lines in the xy plane
Examples:
2555class Ruler2D(vtk.vtkAxisActor2D): 2556 """ 2557 Create a ruler with tick marks, labels and a title. 2558 """ 2559 2560 def __init__( 2561 self, 2562 lw=2, 2563 ticks=True, 2564 labels=False, 2565 c="k", 2566 alpha=1, 2567 title="", 2568 font="Calco", 2569 font_size=24, 2570 bc=None, 2571 ): 2572 """ 2573 Create a ruler with tick marks, labels and a title. 2574 2575 Ruler2D is a 2D actor; that is, it is drawn on the overlay 2576 plane and is not occluded by 3D geometry. 2577 To use this class, specify two points defining the start and end 2578 with update_points() as 3D points. 2579 2580 This class decides decides how to create reasonable tick 2581 marks and labels. 2582 2583 Labels are drawn on the "right" side of the axis. 2584 The "right" side is the side of the axis on the right. 2585 The way the labels and title line up with the axis and tick marks 2586 depends on whether the line is considered horizontal or vertical. 2587 2588 Arguments: 2589 lw : (int) 2590 width of the line in pixel units 2591 ticks : (bool) 2592 control if drawing the tick marks 2593 labels : (bool) 2594 control if drawing the numeric labels 2595 c : (color) 2596 color of the object 2597 alpha : (float) 2598 opacity of the object 2599 title : (str) 2600 title of the ruler 2601 font : (str) 2602 font face name. Check [available fonts here](https://vedo.embl.es/fonts). 2603 font_size : (int) 2604 font size 2605 bc : (color) 2606 background color of the title 2607 2608 Example: 2609 ```python 2610 from vedo import * 2611 plt = Plotter(axes=1, interactive=False) 2612 plt.show(Cube()) 2613 rul = Ruler2D() 2614 rul.set_points([0,0,0], [0.5,0.5,0.5]) 2615 plt.add(rul) 2616 plt.interactive().close() 2617 ``` 2618 ![](https://vedo.embl.es/images/feats/dist_tool.png) 2619 """ 2620 vtk.vtkAxisActor2D.__init__(self) 2621 2622 plt = vedo.plotter_instance 2623 if not plt: 2624 vedo.logger.error("Ruler2D need to initialize Plotter first.") 2625 raise RuntimeError() 2626 2627 self.p0 = [0, 0, 0] 2628 self.p1 = [0, 0, 0] 2629 self.distance = 0 2630 self.title = title 2631 2632 prop = self.GetProperty() 2633 tprop = self.GetTitleTextProperty() 2634 2635 self.SetTitle(title) 2636 self.SetNumberOfLabels(9) 2637 2638 if not font: 2639 font = settings.default_font 2640 if font.lower() == "courier": 2641 tprop.SetFontFamilyToCourier() 2642 elif font.lower() == "times": 2643 tprop.SetFontFamilyToTimes() 2644 elif font.lower() == "arial": 2645 tprop.SetFontFamilyToArial() 2646 else: 2647 tprop.SetFontFamily(vtk.VTK_FONT_FILE) 2648 tprop.SetFontFile(utils.get_font_path(font)) 2649 tprop.SetFontSize(font_size) 2650 tprop.BoldOff() 2651 tprop.ItalicOff() 2652 tprop.ShadowOff() 2653 tprop.SetColor(get_color(c)) 2654 tprop.SetOpacity(alpha) 2655 if bc is not None: 2656 bc = get_color(bc) 2657 tprop.SetBackgroundColor(bc) 2658 tprop.SetBackgroundOpacity(alpha) 2659 2660 lprop = vtk.vtkTextProperty() 2661 lprop.ShallowCopy(tprop) 2662 self.SetLabelTextProperty(lprop) 2663 2664 self.SetLabelFormat("%0.3g") 2665 self.SetTickVisibility(ticks) 2666 self.SetLabelVisibility(labels) 2667 prop.SetLineWidth(lw) 2668 prop.SetColor(get_color(c)) 2669 2670 self.renderer = plt.renderer 2671 self.cid = plt.interactor.AddObserver("RenderEvent", self._update_viz, 1.0) 2672 2673 def color(self, c): 2674 """Assign a new color.""" 2675 c = get_color(c) 2676 self.GetTitleTextProperty().SetColor(c) 2677 self.GetLabelTextProperty().SetColor(c) 2678 self.GetProperty().SetColor(c) 2679 return self 2680 2681 def off(self): 2682 """Switch off the ruler completely.""" 2683 self.renderer.RemoveObserver(self.cid) 2684 self.renderer.RemoveActor(self) 2685 2686 def set_points(self, p0, p1): 2687 """Set new values for the ruler start and end points.""" 2688 self.p0 = np.asarray(p0) 2689 self.p1 = np.asarray(p1) 2690 self._update_viz(0, 0) 2691 return self 2692 2693 def _update_viz(self, evt, name): 2694 ren = self.renderer 2695 view_size = np.array(ren.GetSize()) 2696 2697 ren.SetWorldPoint(*self.p0, 1) 2698 ren.WorldToDisplay() 2699 disp_point1 = ren.GetDisplayPoint()[:2] 2700 disp_point1 = np.array(disp_point1) / view_size 2701 2702 ren.SetWorldPoint(*self.p1, 1) 2703 ren.WorldToDisplay() 2704 disp_point2 = ren.GetDisplayPoint()[:2] 2705 disp_point2 = np.array(disp_point2) / view_size 2706 2707 self.SetPoint1(*disp_point1) 2708 self.SetPoint2(*disp_point2) 2709 self.distance = np.linalg.norm(self.p1 - self.p0) 2710 self.SetRange(0, self.distance) 2711 if not self.title: 2712 self.SetTitle(utils.precision(self.distance, 3))
Create a ruler with tick marks, labels and a title.
2560 def __init__( 2561 self, 2562 lw=2, 2563 ticks=True, 2564 labels=False, 2565 c="k", 2566 alpha=1, 2567 title="", 2568 font="Calco", 2569 font_size=24, 2570 bc=None, 2571 ): 2572 """ 2573 Create a ruler with tick marks, labels and a title. 2574 2575 Ruler2D is a 2D actor; that is, it is drawn on the overlay 2576 plane and is not occluded by 3D geometry. 2577 To use this class, specify two points defining the start and end 2578 with update_points() as 3D points. 2579 2580 This class decides decides how to create reasonable tick 2581 marks and labels. 2582 2583 Labels are drawn on the "right" side of the axis. 2584 The "right" side is the side of the axis on the right. 2585 The way the labels and title line up with the axis and tick marks 2586 depends on whether the line is considered horizontal or vertical. 2587 2588 Arguments: 2589 lw : (int) 2590 width of the line in pixel units 2591 ticks : (bool) 2592 control if drawing the tick marks 2593 labels : (bool) 2594 control if drawing the numeric labels 2595 c : (color) 2596 color of the object 2597 alpha : (float) 2598 opacity of the object 2599 title : (str) 2600 title of the ruler 2601 font : (str) 2602 font face name. Check [available fonts here](https://vedo.embl.es/fonts). 2603 font_size : (int) 2604 font size 2605 bc : (color) 2606 background color of the title 2607 2608 Example: 2609 ```python 2610 from vedo import * 2611 plt = Plotter(axes=1, interactive=False) 2612 plt.show(Cube()) 2613 rul = Ruler2D() 2614 rul.set_points([0,0,0], [0.5,0.5,0.5]) 2615 plt.add(rul) 2616 plt.interactive().close() 2617 ``` 2618 ![](https://vedo.embl.es/images/feats/dist_tool.png) 2619 """ 2620 vtk.vtkAxisActor2D.__init__(self) 2621 2622 plt = vedo.plotter_instance 2623 if not plt: 2624 vedo.logger.error("Ruler2D need to initialize Plotter first.") 2625 raise RuntimeError() 2626 2627 self.p0 = [0, 0, 0] 2628 self.p1 = [0, 0, 0] 2629 self.distance = 0 2630 self.title = title 2631 2632 prop = self.GetProperty() 2633 tprop = self.GetTitleTextProperty() 2634 2635 self.SetTitle(title) 2636 self.SetNumberOfLabels(9) 2637 2638 if not font: 2639 font = settings.default_font 2640 if font.lower() == "courier": 2641 tprop.SetFontFamilyToCourier() 2642 elif font.lower() == "times": 2643 tprop.SetFontFamilyToTimes() 2644 elif font.lower() == "arial": 2645 tprop.SetFontFamilyToArial() 2646 else: 2647 tprop.SetFontFamily(vtk.VTK_FONT_FILE) 2648 tprop.SetFontFile(utils.get_font_path(font)) 2649 tprop.SetFontSize(font_size) 2650 tprop.BoldOff() 2651 tprop.ItalicOff() 2652 tprop.ShadowOff() 2653 tprop.SetColor(get_color(c)) 2654 tprop.SetOpacity(alpha) 2655 if bc is not None: 2656 bc = get_color(bc) 2657 tprop.SetBackgroundColor(bc) 2658 tprop.SetBackgroundOpacity(alpha) 2659 2660 lprop = vtk.vtkTextProperty() 2661 lprop.ShallowCopy(tprop) 2662 self.SetLabelTextProperty(lprop) 2663 2664 self.SetLabelFormat("%0.3g") 2665 self.SetTickVisibility(ticks) 2666 self.SetLabelVisibility(labels) 2667 prop.SetLineWidth(lw) 2668 prop.SetColor(get_color(c)) 2669 2670 self.renderer = plt.renderer 2671 self.cid = plt.interactor.AddObserver("RenderEvent", self._update_viz, 1.0)
Create a ruler with tick marks, labels and a title.
Ruler2D is a 2D actor; that is, it is drawn on the overlay plane and is not occluded by 3D geometry. To use this class, specify two points defining the start and end with update_points() as 3D points.
This class decides decides how to create reasonable tick marks and labels.
Labels are drawn on the "right" side of the axis. The "right" side is the side of the axis on the right. The way the labels and title line up with the axis and tick marks depends on whether the line is considered horizontal or vertical.
Arguments:
- lw : (int) width of the line in pixel units
- ticks : (bool) control if drawing the tick marks
- labels : (bool) control if drawing the numeric labels
- c : (color) color of the object
- alpha : (float) opacity of the object
- title : (str) title of the ruler
- font : (str) font face name. Check available fonts here.
- font_size : (int) font size
- bc : (color) background color of the title
Example:
from vedo import * plt = Plotter(axes=1, interactive=False) plt.show(Cube()) rul = Ruler2D() rul.set_points([0,0,0], [0.5,0.5,0.5]) plt.add(rul) plt.interactive().close()
2673 def color(self, c): 2674 """Assign a new color.""" 2675 c = get_color(c) 2676 self.GetTitleTextProperty().SetColor(c) 2677 self.GetLabelTextProperty().SetColor(c) 2678 self.GetProperty().SetColor(c) 2679 return self
Assign a new color.
2716class DistanceTool(Group): 2717 """ 2718 Create a tool to measure the distance between two clicked points. 2719 """ 2720 2721 def __init__(self, plotter=None, c="k", lw=2): 2722 """ 2723 Create a tool to measure the distance between two clicked points. 2724 2725 Example: 2726 ```python 2727 from vedo import * 2728 mesh = ParametricShape("RandomHills").c("red5") 2729 plt = Plotter(axes=1) 2730 dtool = DistanceTool() 2731 dtool.on() 2732 plt.show(mesh, dtool) 2733 dtool.off() 2734 ``` 2735 ![](https://vedo.embl.es/images/feats/dist_tool.png) 2736 """ 2737 Group.__init__(self) 2738 2739 self.p0 = [0, 0, 0] 2740 self.p1 = [0, 0, 0] 2741 self.distance = 0 2742 if plotter is None: 2743 plotter = vedo.plotter_instance 2744 self.plotter = plotter 2745 self.callback = None 2746 self.cid = None 2747 self.color = c 2748 self.linewidth = lw 2749 self.toggle = True 2750 self.ruler = None 2751 self.title = "" 2752 2753 def on(self): 2754 """Switch tool on.""" 2755 self.cid = self.plotter.add_callback("click", self._onclick) 2756 self.VisibilityOn() 2757 self.plotter.render() 2758 return self 2759 2760 def off(self): 2761 """Switch tool off.""" 2762 self.plotter.remove_callback(self.cid) 2763 self.VisibilityOff() 2764 self.ruler.off() 2765 self.plotter.render() 2766 2767 def _onclick(self, event): 2768 if not event.actor: 2769 return 2770 2771 self.clear() 2772 2773 acts = [] 2774 if self.toggle: 2775 self.p0 = event.picked3d 2776 acts.append(Point(self.p0, c=self.color)) 2777 else: 2778 self.p1 = event.picked3d 2779 self.distance = np.linalg.norm(self.p1 - self.p0) 2780 acts.append(Point(self.p0, c=self.color)) 2781 acts.append(Point(self.p1, c=self.color)) 2782 self.ruler = Ruler2D(c=self.color) 2783 self.ruler.set_points(self.p0, self.p1) 2784 acts.append(self.ruler) 2785 2786 if self.callback is not None: 2787 self.callback(event) 2788 2789 self += acts 2790 self.toggle = not self.toggle
Create a tool to measure the distance between two clicked points.
2721 def __init__(self, plotter=None, c="k", lw=2): 2722 """ 2723 Create a tool to measure the distance between two clicked points. 2724 2725 Example: 2726 ```python 2727 from vedo import * 2728 mesh = ParametricShape("RandomHills").c("red5") 2729 plt = Plotter(axes=1) 2730 dtool = DistanceTool() 2731 dtool.on() 2732 plt.show(mesh, dtool) 2733 dtool.off() 2734 ``` 2735 ![](https://vedo.embl.es/images/feats/dist_tool.png) 2736 """ 2737 Group.__init__(self) 2738 2739 self.p0 = [0, 0, 0] 2740 self.p1 = [0, 0, 0] 2741 self.distance = 0 2742 if plotter is None: 2743 plotter = vedo.plotter_instance 2744 self.plotter = plotter 2745 self.callback = None 2746 self.cid = None 2747 self.color = c 2748 self.linewidth = lw 2749 self.toggle = True 2750 self.ruler = None 2751 self.title = ""
Create a tool to measure the distance between two clicked points.
Example:
from vedo import * mesh = ParametricShape("RandomHills").c("red5") plt = Plotter(axes=1) dtool = DistanceTool() dtool.on() plt.show(mesh, dtool) dtool.off()
2753 def on(self): 2754 """Switch tool on.""" 2755 self.cid = self.plotter.add_callback("click", self._onclick) 2756 self.VisibilityOn() 2757 self.plotter.render() 2758 return self
Switch tool on.
2760 def off(self): 2761 """Switch tool off.""" 2762 self.plotter.remove_callback(self.cid) 2763 self.VisibilityOff() 2764 self.ruler.off() 2765 self.plotter.render()
Switch tool off.
Inherited Members
527class SplineTool(vtk.vtkContourWidget): 528 """ 529 Spline tool, draw a spline through a set of points interactively. 530 """ 531 532 def __init__(self, points, pc="k", ps=8, lc="r4", ac="g5", lw=2, closed=False, ontop=True): 533 """ 534 Spline tool, draw a spline through a set of points interactively. 535 536 Arguments: 537 points : (list), Points 538 initial set of points. 539 pc : (str) 540 point color. 541 ps : (int) 542 point size. 543 lc : (str) 544 line color. 545 ac : (str) 546 active point color. 547 lw : (int) 548 line width. 549 closed : (bool) 550 spline is closed or open. 551 ontop : (bool) 552 show it always on top of other objects. 553 554 Examples: 555 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 556 557 ![](https://vedo.embl.es/images/basic/spline_tool.png) 558 """ 559 vtk.vtkContourWidget.__init__(self) 560 561 self.representation = vtk.vtkOrientedGlyphContourRepresentation() 562 self.representation.SetAlwaysOnTop(ontop) 563 564 self.representation.GetLinesProperty().SetColor(get_color(lc)) 565 self.representation.GetLinesProperty().SetLineWidth(lw) 566 567 self.representation.GetProperty().SetColor(get_color(pc)) 568 self.representation.GetProperty().SetPointSize(ps) 569 self.representation.GetProperty().RenderPointsAsSpheresOn() 570 571 self.representation.GetActiveProperty().SetColor(get_color(ac)) 572 self.representation.GetActiveProperty().SetLineWidth(lw + 1) 573 574 self.SetRepresentation(self.representation) 575 576 if utils.is_sequence(points): 577 self.points = Points(points) 578 else: 579 self.points = points 580 581 self.closed = closed 582 583 def add(self, pt): 584 """ 585 Add one point at a specified position in space if 3D, 586 or 2D screen-display position if 2D. 587 """ 588 if len(pt) == 2: 589 self.representation.AddNodeAtDisplayPosition(int(pt[0]), int(pt[1])) 590 else: 591 self.representation.AddNodeAtWorldPosition(pt) 592 return self 593 594 def remove(self, i): 595 """Remove specific node by its index""" 596 self.representation.DeleteNthNode(i) 597 return self 598 599 def on(self): 600 """Activate/Enable the tool""" 601 self.On() 602 self.Render() 603 return self 604 605 def off(self): 606 """Disactivate/Disable the tool""" 607 self.Off() 608 self.Render() 609 return self 610 611 def render(self): 612 """Render the spline""" 613 self.Render() 614 return self 615 616 def bounds(self): 617 """Retrieve the bounding box of the spline as [x0,x1, y0,y1, z0,z1]""" 618 return self.GetBounds() 619 620 def spline(self): 621 """Return the vedo.Spline object.""" 622 self.representation.SetClosedLoop(self.closed) 623 self.representation.BuildRepresentation() 624 pd = self.representation.GetContourRepresentationAsPolyData() 625 pts = utils.vtk2numpy(pd.GetPoints().GetData()) 626 ln = vedo.Line(pts, lw=2, c="k") 627 return ln 628 629 def nodes(self, onscreen=False): 630 """Return the current position in space (or on 2D screen-display) of the spline nodes.""" 631 n = self.representation.GetNumberOfNodes() 632 pts = [] 633 for i in range(n): 634 p = [0.0, 0.0, 0.0] 635 if onscreen: 636 self.representation.GetNthNodeDisplayPosition(i, p) 637 else: 638 self.representation.GetNthNodeWorldPosition(i, p) 639 pts.append(p) 640 return np.array(pts)
Spline tool, draw a spline through a set of points interactively.
532 def __init__(self, points, pc="k", ps=8, lc="r4", ac="g5", lw=2, closed=False, ontop=True): 533 """ 534 Spline tool, draw a spline through a set of points interactively. 535 536 Arguments: 537 points : (list), Points 538 initial set of points. 539 pc : (str) 540 point color. 541 ps : (int) 542 point size. 543 lc : (str) 544 line color. 545 ac : (str) 546 active point color. 547 lw : (int) 548 line width. 549 closed : (bool) 550 spline is closed or open. 551 ontop : (bool) 552 show it always on top of other objects. 553 554 Examples: 555 - [spline_tool.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/spline_tool.py) 556 557 ![](https://vedo.embl.es/images/basic/spline_tool.png) 558 """ 559 vtk.vtkContourWidget.__init__(self) 560 561 self.representation = vtk.vtkOrientedGlyphContourRepresentation() 562 self.representation.SetAlwaysOnTop(ontop) 563 564 self.representation.GetLinesProperty().SetColor(get_color(lc)) 565 self.representation.GetLinesProperty().SetLineWidth(lw) 566 567 self.representation.GetProperty().SetColor(get_color(pc)) 568 self.representation.GetProperty().SetPointSize(ps) 569 self.representation.GetProperty().RenderPointsAsSpheresOn() 570 571 self.representation.GetActiveProperty().SetColor(get_color(ac)) 572 self.representation.GetActiveProperty().SetLineWidth(lw + 1) 573 574 self.SetRepresentation(self.representation) 575 576 if utils.is_sequence(points): 577 self.points = Points(points) 578 else: 579 self.points = points 580 581 self.closed = closed
Spline tool, draw a spline through a set of points interactively.
Arguments:
- points : (list), Points initial set of points.
- pc : (str) point color.
- ps : (int) point size.
- lc : (str) line color.
- ac : (str) active point color.
- lw : (int) line width.
- closed : (bool) spline is closed or open.
- ontop : (bool) show it always on top of other objects.
Examples:
583 def add(self, pt): 584 """ 585 Add one point at a specified position in space if 3D, 586 or 2D screen-display position if 2D. 587 """ 588 if len(pt) == 2: 589 self.representation.AddNodeAtDisplayPosition(int(pt[0]), int(pt[1])) 590 else: 591 self.representation.AddNodeAtWorldPosition(pt) 592 return self
Add one point at a specified position in space if 3D, or 2D screen-display position if 2D.
594 def remove(self, i): 595 """Remove specific node by its index""" 596 self.representation.DeleteNthNode(i) 597 return self
Remove specific node by its index
599 def on(self): 600 """Activate/Enable the tool""" 601 self.On() 602 self.Render() 603 return self
Activate/Enable the tool
605 def off(self): 606 """Disactivate/Disable the tool""" 607 self.Off() 608 self.Render() 609 return self
Disactivate/Disable the tool
616 def bounds(self): 617 """Retrieve the bounding box of the spline as [x0,x1, y0,y1, z0,z1]""" 618 return self.GetBounds()
Retrieve the bounding box of the spline as [x0,x1, y0,y1, z0,z1]
620 def spline(self): 621 """Return the vedo.Spline object.""" 622 self.representation.SetClosedLoop(self.closed) 623 self.representation.BuildRepresentation() 624 pd = self.representation.GetContourRepresentationAsPolyData() 625 pts = utils.vtk2numpy(pd.GetPoints().GetData()) 626 ln = vedo.Line(pts, lw=2, c="k") 627 return ln
Return the vedo.Spline object.
629 def nodes(self, onscreen=False): 630 """Return the current position in space (or on 2D screen-display) of the spline nodes.""" 631 n = self.representation.GetNumberOfNodes() 632 pts = [] 633 for i in range(n): 634 p = [0.0, 0.0, 0.0] 635 if onscreen: 636 self.representation.GetNthNodeDisplayPosition(i, p) 637 else: 638 self.representation.GetNthNodeWorldPosition(i, p) 639 pts.append(p) 640 return np.array(pts)
Return the current position in space (or on 2D screen-display) of the spline nodes.
707def Goniometer( 708 p1, 709 p2, 710 p3, 711 font="", 712 arc_size=0.4, 713 s=1, 714 italic=0, 715 rotation=0, 716 prefix="", 717 lc="k2", 718 c="white", 719 alpha=1, 720 lw=2, 721 precision=3, 722): 723 """ 724 Build a graphical goniometer to measure the angle formed by 3 points in space. 725 726 Arguments: 727 p1 : (list) 728 first point 3D coordinates. 729 p2 : (list) 730 the vertex point. 731 p3 : (list) 732 the last point defining the angle. 733 font : (str) 734 Font face. Check [available fonts here](https://vedo.embl.es/fonts). 735 arc_size : (float) 736 dimension of the arc wrt the smallest axis. 737 s : (float) 738 size of the text. 739 italic : (float, bool) 740 italic text. 741 rotation : (float) 742 rotation of text in degrees. 743 prefix : (str) 744 append this string to the numeric value of the angle. 745 lc : (list) 746 color of the goniometer lines. 747 c : (str) 748 color of the goniometer angle filling. Set alpha=0 to remove it. 749 alpha : (float) 750 transparency level. 751 lw : (float) 752 line width. 753 precision : (int) 754 number of significant digits. 755 756 Examples: 757 - [goniometer.py](https://github.com/marcomusy/vedo/tree/master/examples/pyplot/goniometer.py) 758 759 ![](https://vedo.embl.es/images/pyplot/goniometer.png) 760 """ 761 if isinstance(p1, Points): p1 = p1.GetPosition() 762 if isinstance(p2, Points): p2 = p2.GetPosition() 763 if isinstance(p3, Points): p3 = p3.GetPosition() 764 if len(p1)==2: p1=[p1[0], p1[1], 0.0] 765 if len(p2)==2: p2=[p2[0], p2[1], 0.0] 766 if len(p3)==2: p3=[p3[0], p3[1], 0.0] 767 p1, p2, p3 = np.array(p1), np.array(p2), np.array(p3) 768 769 acts = [] 770 ln = shapes.Line([p1, p2, p3], lw=lw, c=lc) 771 acts.append(ln) 772 773 va = utils.versor(p1 - p2) 774 vb = utils.versor(p3 - p2) 775 r = min(utils.mag(p3 - p2), utils.mag(p1 - p2)) * arc_size 776 ptsarc = [] 777 res = 120 778 imed = int(res / 2) 779 for i in range(res + 1): 780 vi = utils.versor(vb * i / res + va * (res - i) / res) 781 if i == imed: 782 vc = np.array(vi) 783 ptsarc.append(p2 + vi * r) 784 arc = shapes.Line(ptsarc).lw(lw).c(lc) 785 acts.append(arc) 786 787 angle = np.arccos(np.dot(va, vb)) * 180 / np.pi 788 789 lb = shapes.Text3D( 790 prefix + utils.precision(angle, precision) + "º", 791 s=r / 12 * s, 792 font=font, 793 italic=italic, 794 justify="center", 795 ) 796 cr = np.cross(va, vb) 797 lb.pos(p2 + vc * r / 1.75).orientation(cr * np.sign(cr[2]), rotation=rotation) 798 lb.c(c).bc("tomato").lighting("off") 799 acts.append(lb) 800 801 if alpha > 0: 802 pts = [p2] + arc.points().tolist() + [p2] 803 msh = Mesh([pts, [list(range(arc.npoints + 2))]], c=lc, alpha=alpha) 804 msh.lighting("off") 805 msh.triangulate() 806 msh.shift(0, 0, -r / 10000) # to resolve 2d conflicts.. 807 acts.append(msh) 808 809 asse = Assembly(acts) 810 return asse
Build a graphical goniometer to measure the angle formed by 3 points in space.
Arguments:
- p1 : (list) first point 3D coordinates.
- p2 : (list) the vertex point.
- p3 : (list) the last point defining the angle.
- font : (str) Font face. Check available fonts here.
- arc_size : (float) dimension of the arc wrt the smallest axis.
- s : (float) size of the text.
- italic : (float, bool) italic text.
- rotation : (float) rotation of text in degrees.
- prefix : (str) append this string to the numeric value of the angle.
- lc : (list) color of the goniometer lines.
- c : (str) color of the goniometer angle filling. Set alpha=0 to remove it.
- alpha : (float) transparency level.
- lw : (float) line width.
- precision : (int) number of significant digits.
Examples:
351class Button(vtk.vtkTextActor): 352 """ 353 Build a Button object. 354 """ 355 def __init__( 356 self, 357 fnc=None, 358 states=("Button"), 359 c=("white"), 360 bc=("green4"), 361 pos=(0.7, 0.05), 362 size=24, 363 font=None, 364 bold=False, 365 italic=False, 366 alpha=1, 367 angle=0, 368 name="Button", 369 ): 370 """ 371 Build a Button object to be shown in the rendering window. 372 373 Arguments: 374 fnc : (function) 375 external function to be called by the widget 376 states : (list) 377 the list of possible states, eg. ['On', 'Off'] 378 c : (list) 379 the list of colors for each state eg. ['red3', 'green5'] 380 bc : (list) 381 the list of background colors for each state 382 pos : (list, str) 383 2D position in pixels from left-bottom corner 384 size : (int) 385 size of button font 386 font : (str) 387 font type 388 bold : (bool) 389 set bold font face 390 italic : (bool) 391 italic font face 392 alpha : (float) 393 opacity level 394 angle : (float) 395 anticlockwise rotation in degrees 396 name : (str) 397 name of the button (useful for multiple buttons in callbacks) 398 399 Examples: 400 - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py) 401 402 Examples: 403 - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py) 404 405 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 406 407 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 408 409 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 410 """ 411 vtk.vtkTextActor.__init__(self) 412 413 self.status_idx = 0 414 self.states = states 415 416 if not utils.is_sequence(c): 417 c = [c] 418 self.colors = c 419 420 if not utils.is_sequence(bc): 421 bc = [bc] 422 self.bcolors = bc 423 424 assert len(c) == len(bc), "in Button color number mismatch!" 425 426 self.function = fnc 427 self.function_id = None 428 self.name = name 429 430 self.GetActualPositionCoordinate().SetCoordinateSystemToNormalizedViewport() 431 self.SetPosition(pos[0], pos[1]) 432 433 self.offset = 5 434 self.spacer = " " 435 436 self.len_states = max([len(s) for s in states]) 437 438 self.text_property = self.GetTextProperty() 439 self.text_property.SetJustificationToCentered() 440 441 if not font: 442 font = settings.default_font 443 444 if font.lower() == "courier": 445 self.text_property.SetFontFamilyToCourier() 446 elif font.lower() == "times": 447 self.text_property.SetFontFamilyToTimes() 448 elif font.lower() == "arial": 449 self.text_property.SetFontFamilyToArial() 450 else: 451 self.text_property.SetFontFamily(vtk.VTK_FONT_FILE) 452 self.text_property.SetFontFile(utils.get_font_path(font)) 453 self.text_property.SetFontSize(size) 454 455 self.text_property.SetBackgroundOpacity(alpha) 456 457 self.text_property.BoldOff() 458 if bold: 459 self.text_property.BoldOn() 460 461 self.text_property.ItalicOff() 462 if italic: 463 self.text_property.ItalicOn() 464 465 self.text_property.ShadowOff() 466 self.text_property.SetOrientation(angle) 467 self.text_property.SetLineOffset(self.offset) 468 469 self.hasframe = hasattr(self.text_property, "FrameOn") 470 471 self.status(0) 472 473 def text(self, txt="", c=None): 474 if txt: 475 self.SetInput(self.spacer + str(txt) + self.spacer) 476 else: 477 return self.GetInput() 478 479 if c is not None: 480 self.text_property.SetColor(get_color(c)) 481 return self 482 483 def backcolor(self, c): 484 self.text_property.SetBackgroundColor(get_color(c)) 485 return self 486 487 def frame(self, lw=None, c=None): 488 if self.hasframe: 489 self.text_property.FrameOn() 490 if lw is not None: 491 if lw > 0: 492 self.text_property.FrameOn() 493 self.text_property.SetFrameWidth(lw) 494 else: 495 self.text_property.FrameOff() 496 return self 497 if c is not None: 498 self.text_property.SetFrameColor(get_color(c)) 499 return self 500 501 def status(self, s=None): 502 """ 503 Set/Get the status of the button. 504 """ 505 if s is None: 506 return self.states[self.status_idx] 507 508 if isinstance(s, str): 509 s = self.states.index(s) 510 self.status_idx = s 511 self.text(self.states[s]) 512 s = s % len(self.bcolors) 513 self.text(c=self.colors[s]) 514 self.backcolor(self.bcolors[s]) 515 return self 516 517 def switch(self): 518 """ 519 Change/cycle button status to the next defined status in states list. 520 """ 521 self.status_idx = (self.status_idx + 1) % len(self.states) 522 self.status(self.status_idx) 523 return self
Build a Button object.
355 def __init__( 356 self, 357 fnc=None, 358 states=("Button"), 359 c=("white"), 360 bc=("green4"), 361 pos=(0.7, 0.05), 362 size=24, 363 font=None, 364 bold=False, 365 italic=False, 366 alpha=1, 367 angle=0, 368 name="Button", 369 ): 370 """ 371 Build a Button object to be shown in the rendering window. 372 373 Arguments: 374 fnc : (function) 375 external function to be called by the widget 376 states : (list) 377 the list of possible states, eg. ['On', 'Off'] 378 c : (list) 379 the list of colors for each state eg. ['red3', 'green5'] 380 bc : (list) 381 the list of background colors for each state 382 pos : (list, str) 383 2D position in pixels from left-bottom corner 384 size : (int) 385 size of button font 386 font : (str) 387 font type 388 bold : (bool) 389 set bold font face 390 italic : (bool) 391 italic font face 392 alpha : (float) 393 opacity level 394 angle : (float) 395 anticlockwise rotation in degrees 396 name : (str) 397 name of the button (useful for multiple buttons in callbacks) 398 399 Examples: 400 - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py) 401 402 Examples: 403 - [buttons.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/buttons.py) 404 405 ![](https://user-images.githubusercontent.com/32848391/50738870-c0fe2500-11d8-11e9-9b78-92754f5c5968.jpg) 406 407 - [timer_callback2.py](https://github.com/marcomusy/vedo/tree/master/examples/advanced/timer_callback2.py) 408 409 ![](https://vedo.embl.es/images/advanced/timer_callback1.jpg) 410 """ 411 vtk.vtkTextActor.__init__(self) 412 413 self.status_idx = 0 414 self.states = states 415 416 if not utils.is_sequence(c): 417 c = [c] 418 self.colors = c 419 420 if not utils.is_sequence(bc): 421 bc = [bc] 422 self.bcolors = bc 423 424 assert len(c) == len(bc), "in Button color number mismatch!" 425 426 self.function = fnc 427 self.function_id = None 428 self.name = name 429 430 self.GetActualPositionCoordinate().SetCoordinateSystemToNormalizedViewport() 431 self.SetPosition(pos[0], pos[1]) 432 433 self.offset = 5 434 self.spacer = " " 435 436 self.len_states = max([len(s) for s in states]) 437 438 self.text_property = self.GetTextProperty() 439 self.text_property.SetJustificationToCentered() 440 441 if not font: 442 font = settings.default_font 443 444 if font.lower() == "courier": 445 self.text_property.SetFontFamilyToCourier() 446 elif font.lower() == "times": 447 self.text_property.SetFontFamilyToTimes() 448 elif font.lower() == "arial": 449 self.text_property.SetFontFamilyToArial() 450 else: 451 self.text_property.SetFontFamily(vtk.VTK_FONT_FILE) 452 self.text_property.SetFontFile(utils.get_font_path(font)) 453 self.text_property.SetFontSize(size) 454 455 self.text_property.SetBackgroundOpacity(alpha) 456 457 self.text_property.BoldOff() 458 if bold: 459 self.text_property.BoldOn() 460 461 self.text_property.ItalicOff() 462 if italic: 463 self.text_property.ItalicOn() 464 465 self.text_property.ShadowOff() 466 self.text_property.SetOrientation(angle) 467 self.text_property.SetLineOffset(self.offset) 468 469 self.hasframe = hasattr(self.text_property, "FrameOn") 470 471 self.status(0)
Build a Button object to be shown in the rendering window.
Arguments:
- fnc : (function) external function to be called by the widget
- states : (list) the list of possible states, eg. ['On', 'Off']
- c : (list) the list of colors for each state eg. ['red3', 'green5']
- bc : (list) the list of background colors for each state
- pos : (list, str) 2D position in pixels from left-bottom corner
- size : (int) size of button font
- font : (str) font type
- bold : (bool) set bold font face
- italic : (bool) italic font face
- alpha : (float) opacity level
- angle : (float) anticlockwise rotation in degrees
- name : (str) name of the button (useful for multiple buttons in callbacks)
Examples:
Examples:
487 def frame(self, lw=None, c=None): 488 if self.hasframe: 489 self.text_property.FrameOn() 490 if lw is not None: 491 if lw > 0: 492 self.text_property.FrameOn() 493 self.text_property.SetFrameWidth(lw) 494 else: 495 self.text_property.FrameOff() 496 return self 497 if c is not None: 498 self.text_property.SetFrameColor(get_color(c)) 499 return self
501 def status(self, s=None): 502 """ 503 Set/Get the status of the button. 504 """ 505 if s is None: 506 return self.states[self.status_idx] 507 508 if isinstance(s, str): 509 s = self.states.index(s) 510 self.status_idx = s 511 self.text(self.states[s]) 512 s = s % len(self.bcolors) 513 self.text(c=self.colors[s]) 514 self.backcolor(self.bcolors[s]) 515 return self
Set/Get the status of the button.
517 def switch(self): 518 """ 519 Change/cycle button status to the next defined status in states list. 520 """ 521 self.status_idx = (self.status_idx + 1) % len(self.states) 522 self.status(self.status_idx) 523 return self
Change/cycle button status to the next defined status in states list.
55class Flagpost(vtk.vtkFlagpoleLabel): 56 """ 57 Create a flag post style element to describe an object. 58 """ 59 60 def __init__( 61 self, 62 txt="", 63 base=(0, 0, 0), 64 top=(0, 0, 1), 65 s=1, 66 c="k9", 67 bc="k1", 68 alpha=1, 69 lw=0, 70 font="Calco", 71 justify="center-left", 72 vspacing=1, 73 ): 74 """ 75 Create a flag post style element to describe an object. 76 77 Arguments: 78 txt : (str) 79 Text to display. The default is the filename or the object name. 80 base : (list) 81 position of the flag anchor point. 82 top : (list) 83 a 3D displacement or offset. 84 s : (float) 85 size of the text to be shown 86 c : (list) 87 color of text and line 88 bc : (list) 89 color of the flag background 90 alpha : (float) 91 opacity of text and box. 92 lw : (int) 93 line with of box frame. The default is 0. 94 font : (str) 95 font name. Use a monospace font for better rendering. The default is "Calco". 96 Type `vedo -r fonts` for a font demo. 97 Check [available fonts here](https://vedo.embl.es/fonts). 98 justify : (str) 99 internal text justification. The default is "center-left". 100 vspacing : (float) 101 vertical spacing between lines. 102 103 Examples: 104 - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/examples/other/flag_labels2.py) 105 106 ![](https://vedo.embl.es/images/other/flag_labels2.png) 107 """ 108 109 vtk.vtkFlagpoleLabel.__init__(self) 110 111 base = utils.make3d(base) 112 top = utils.make3d(top) 113 114 self.SetBasePosition(*base) 115 self.SetTopPosition(*top) 116 117 self.SetFlagSize(s) 118 self.SetInput(txt) 119 self.PickableOff() 120 121 self.GetProperty().LightingOff() 122 self.GetProperty().SetLineWidth(lw + 1) 123 124 prop = self.GetTextProperty() 125 if bc is not None: 126 prop.SetBackgroundColor(get_color(bc)) 127 128 prop.SetOpacity(alpha) 129 prop.SetBackgroundOpacity(alpha) 130 if bc is not None and len(bc) == 4: 131 prop.SetBackgroundRGBA(alpha) 132 133 c = get_color(c) 134 prop.SetColor(c) 135 self.GetProperty().SetColor(c) 136 137 prop.SetFrame(bool(lw)) 138 prop.SetFrameWidth(lw) 139 prop.SetFrameColor(prop.GetColor()) 140 141 prop.SetFontFamily(vtk.VTK_FONT_FILE) 142 fl = utils.get_font_path(font) 143 prop.SetFontFile(fl) 144 prop.ShadowOff() 145 prop.BoldOff() 146 prop.SetOpacity(alpha) 147 prop.SetJustificationToLeft() 148 if "top" in justify: 149 prop.SetVerticalJustificationToTop() 150 if "bottom" in justify: 151 prop.SetVerticalJustificationToBottom() 152 if "cent" in justify: 153 prop.SetVerticalJustificationToCentered() 154 prop.SetJustificationToCentered() 155 if "left" in justify: 156 prop.SetJustificationToLeft() 157 if "right" in justify: 158 prop.SetJustificationToRight() 159 prop.SetLineSpacing(vspacing * 1.2) 160 self.SetUseBounds(False) 161 162 def text(self, value): 163 self.SetInput(value) 164 return self 165 166 def on(self): 167 self.VisibilityOn() 168 return self 169 170 def off(self): 171 self.VisibilityOff() 172 return self 173 174 def toggle(self): 175 self.SetVisibility(not self.GetVisibility()) 176 return self 177 178 def use_bounds(self, value=True): 179 self.SetUseBounds(value) 180 return self 181 182 def color(self, c): 183 c = get_color(c) 184 self.GetTextProperty().SetColor(c) 185 self.GetProperty().SetColor(c) 186 return self 187 188 def pos(self, p): 189 p = np.asarray(p) 190 self.top = self.top - self.base + p 191 self.base = p 192 return self 193 194 @property 195 def base(self): 196 return np.array(self.GetBasePosition()) 197 198 @property 199 def top(self): 200 return np.array(self.GetTopPosition()) 201 202 @base.setter 203 def base(self, value): 204 self.SetBasePosition(*value) 205 206 @top.setter 207 def top(self, value): 208 self.SetTopPosition(*value)
Create a flag post style element to describe an object.
60 def __init__( 61 self, 62 txt="", 63 base=(0, 0, 0), 64 top=(0, 0, 1), 65 s=1, 66 c="k9", 67 bc="k1", 68 alpha=1, 69 lw=0, 70 font="Calco", 71 justify="center-left", 72 vspacing=1, 73 ): 74 """ 75 Create a flag post style element to describe an object. 76 77 Arguments: 78 txt : (str) 79 Text to display. The default is the filename or the object name. 80 base : (list) 81 position of the flag anchor point. 82 top : (list) 83 a 3D displacement or offset. 84 s : (float) 85 size of the text to be shown 86 c : (list) 87 color of text and line 88 bc : (list) 89 color of the flag background 90 alpha : (float) 91 opacity of text and box. 92 lw : (int) 93 line with of box frame. The default is 0. 94 font : (str) 95 font name. Use a monospace font for better rendering. The default is "Calco". 96 Type `vedo -r fonts` for a font demo. 97 Check [available fonts here](https://vedo.embl.es/fonts). 98 justify : (str) 99 internal text justification. The default is "center-left". 100 vspacing : (float) 101 vertical spacing between lines. 102 103 Examples: 104 - [flag_labels2.py](https://github.com/marcomusy/vedo/tree/master/examples/examples/other/flag_labels2.py) 105 106 ![](https://vedo.embl.es/images/other/flag_labels2.png) 107 """ 108 109 vtk.vtkFlagpoleLabel.__init__(self) 110 111 base = utils.make3d(base) 112 top = utils.make3d(top) 113 114 self.SetBasePosition(*base) 115 self.SetTopPosition(*top) 116 117 self.SetFlagSize(s) 118 self.SetInput(txt) 119 self.PickableOff() 120 121 self.GetProperty().LightingOff() 122 self.GetProperty().SetLineWidth(lw + 1) 123 124 prop = self.GetTextProperty() 125 if bc is not None: 126 prop.SetBackgroundColor(get_color(bc)) 127 128 prop.SetOpacity(alpha) 129 prop.SetBackgroundOpacity(alpha) 130 if bc is not None and len(bc) == 4: 131 prop.SetBackgroundRGBA(alpha) 132 133 c = get_color(c) 134 prop.SetColor(c) 135 self.GetProperty().SetColor(c) 136 137 prop.SetFrame(bool(lw)) 138 prop.SetFrameWidth(lw) 139 prop.SetFrameColor(prop.GetColor()) 140 141 prop.SetFontFamily(vtk.VTK_FONT_FILE) 142 fl = utils.get_font_path(font) 143 prop.SetFontFile(fl) 144 prop.ShadowOff() 145 prop.BoldOff() 146 prop.SetOpacity(alpha) 147 prop.SetJustificationToLeft() 148 if "top" in justify: 149 prop.SetVerticalJustificationToTop() 150 if "bottom" in justify: 151 prop.SetVerticalJustificationToBottom() 152 if "cent" in justify: 153 prop.SetVerticalJustificationToCentered() 154 prop.SetJustificationToCentered() 155 if "left" in justify: 156 prop.SetJustificationToLeft() 157 if "right" in justify: 158 prop.SetJustificationToRight() 159 prop.SetLineSpacing(vspacing * 1.2) 160 self.SetUseBounds(False)
Create a flag post style element to describe an object.
Arguments:
- txt : (str) Text to display. The default is the filename or the object name.
- base : (list) position of the flag anchor point.
- top : (list) a 3D displacement or offset.
- s : (float) size of the text to be shown
- c : (list) color of text and line
- bc : (list) color of the flag background
- alpha : (float) opacity of text and box.
- lw : (int) line with of box frame. The default is 0.
- font : (str)
font name. Use a monospace font for better rendering. The default is "Calco".
Type
vedo -r fonts
for a font demo. Check available fonts here. - justify : (str) internal text justification. The default is "center-left".
- vspacing : (float) vertical spacing between lines.
Examples:
2143class ProgressBarWidget(vtk.vtkActor2D): 2144 """ 2145 Add a progress bar in the rendering window. 2146 """ 2147 def __init__(self, n=None, c="blue5", alpha=0.8, lw=10, autohide=True): 2148 """ 2149 Add a progress bar window. 2150 2151 Arguments: 2152 n : (int) 2153 number of iterations. 2154 If None, you need to call `update(fraction)` manually. 2155 c : (color) 2156 color of the line. 2157 alpha : (float) 2158 opacity of the line. 2159 lw : (int) 2160 line width in pixels. 2161 autohide : (bool) 2162 if True, hide the progress bar when completed. 2163 """ 2164 self.n = 0 2165 self.iterations = n 2166 self.autohide = autohide 2167 2168 ppoints = vtk.vtkPoints() # Generate the line 2169 psqr = [[0, 0, 0], [1, 0, 0]] 2170 for i, pt in enumerate(psqr): 2171 ppoints.InsertPoint(i, *pt) 2172 lines = vtk.vtkCellArray() 2173 lines.InsertNextCell(len(psqr)) 2174 for i in range(len(psqr)): 2175 lines.InsertCellPoint(i) 2176 pd = vtk.vtkPolyData() 2177 pd.SetPoints(ppoints) 2178 pd.SetLines(lines) 2179 self._data = pd 2180 2181 mapper = vtk.vtkPolyDataMapper2D() 2182 mapper.SetInputData(pd) 2183 cs = vtk.vtkCoordinate() 2184 cs.SetCoordinateSystemToNormalizedViewport() 2185 mapper.SetTransformCoordinate(cs) 2186 2187 vtk.vtkActor2D.__init__(self) 2188 2189 self.SetMapper(mapper) 2190 self.GetProperty().SetOpacity(alpha) 2191 self.GetProperty().SetColor(get_color(c)) 2192 self.GetProperty().SetLineWidth(lw*2) 2193 2194 2195 def lw(self, value): 2196 """Set width.""" 2197 self.GetProperty().SetLineWidth(value*2) 2198 return self 2199 2200 def c(self, color): 2201 """Set color.""" 2202 c = get_color(color) 2203 self.GetProperty().SetColor(c) 2204 return self 2205 2206 def alpha(self, value): 2207 """Set opacity.""" 2208 self.GetProperty().SetOpacity(value) 2209 return self 2210 2211 def update(self, fraction=None): 2212 """Update progress bar to fraction of the window width.""" 2213 if fraction is None: 2214 if self.iterations is None: 2215 vedo.printc("Error in ProgressBarWindow: must specify iterations", c='r') 2216 return self 2217 self.n += 1 2218 fraction = self.n / self.iterations 2219 2220 if fraction >= 1 and self.autohide: 2221 fraction = 0 2222 2223 psqr = [[0, 0, 0], [fraction, 0, 0]] 2224 vpts = utils.numpy2vtk(psqr, dtype=np.float32) 2225 self._data.GetPoints().SetData(vpts) 2226 return self 2227 2228 def reset(self): 2229 """Reset progress bar.""" 2230 self.n = 0 2231 self.update(0) 2232 return self
Add a progress bar in the rendering window.
2147 def __init__(self, n=None, c="blue5", alpha=0.8, lw=10, autohide=True): 2148 """ 2149 Add a progress bar window. 2150 2151 Arguments: 2152 n : (int) 2153 number of iterations. 2154 If None, you need to call `update(fraction)` manually. 2155 c : (color) 2156 color of the line. 2157 alpha : (float) 2158 opacity of the line. 2159 lw : (int) 2160 line width in pixels. 2161 autohide : (bool) 2162 if True, hide the progress bar when completed. 2163 """ 2164 self.n = 0 2165 self.iterations = n 2166 self.autohide = autohide 2167 2168 ppoints = vtk.vtkPoints() # Generate the line 2169 psqr = [[0, 0, 0], [1, 0, 0]] 2170 for i, pt in enumerate(psqr): 2171 ppoints.InsertPoint(i, *pt) 2172 lines = vtk.vtkCellArray() 2173 lines.InsertNextCell(len(psqr)) 2174 for i in range(len(psqr)): 2175 lines.InsertCellPoint(i) 2176 pd = vtk.vtkPolyData() 2177 pd.SetPoints(ppoints) 2178 pd.SetLines(lines) 2179 self._data = pd 2180 2181 mapper = vtk.vtkPolyDataMapper2D() 2182 mapper.SetInputData(pd) 2183 cs = vtk.vtkCoordinate() 2184 cs.SetCoordinateSystemToNormalizedViewport() 2185 mapper.SetTransformCoordinate(cs) 2186 2187 vtk.vtkActor2D.__init__(self) 2188 2189 self.SetMapper(mapper) 2190 self.GetProperty().SetOpacity(alpha) 2191 self.GetProperty().SetColor(get_color(c)) 2192 self.GetProperty().SetLineWidth(lw*2)
Add a progress bar window.
Arguments:
- n : (int)
number of iterations.
If None, you need to call
update(fraction)
manually. - c : (color) color of the line.
- alpha : (float) opacity of the line.
- lw : (int) line width in pixels.
- autohide : (bool) if True, hide the progress bar when completed.
2195 def lw(self, value): 2196 """Set width.""" 2197 self.GetProperty().SetLineWidth(value*2) 2198 return self
Set width.
2200 def c(self, color): 2201 """Set color.""" 2202 c = get_color(color) 2203 self.GetProperty().SetColor(c) 2204 return self
Set color.
2206 def alpha(self, value): 2207 """Set opacity.""" 2208 self.GetProperty().SetOpacity(value) 2209 return self
Set opacity.
2211 def update(self, fraction=None): 2212 """Update progress bar to fraction of the window width.""" 2213 if fraction is None: 2214 if self.iterations is None: 2215 vedo.printc("Error in ProgressBarWindow: must specify iterations", c='r') 2216 return self 2217 self.n += 1 2218 fraction = self.n / self.iterations 2219 2220 if fraction >= 1 and self.autohide: 2221 fraction = 0 2222 2223 psqr = [[0, 0, 0], [fraction, 0, 0]] 2224 vpts = utils.numpy2vtk(psqr, dtype=np.float32) 2225 self._data.GetPoints().SetData(vpts) 2226 return self
Update progress bar to fraction of the window width.
1860class BoxCutter(vtk.vtkBoxWidget, BaseCutter): 1861 """ 1862 Create a box widget to cut away parts of a Mesh. 1863 """ 1864 def __init__( 1865 self, 1866 mesh, 1867 invert=False, 1868 can_rotate=True, 1869 can_translate=True, 1870 can_scale=True, 1871 initial_bounds=(), 1872 padding=0.025, 1873 c=(0.25, 0.25, 0.25), 1874 alpha=0.05, 1875 ): 1876 """ 1877 Create a box widget to cut away parts of a Mesh. 1878 1879 Arguments: 1880 mesh : (Mesh) 1881 the input mesh 1882 invert : (bool) 1883 invert the clipping plane 1884 can_rotate : (bool) 1885 enable rotation of the widget 1886 can_translate : (bool) 1887 enable translation of the widget 1888 can_scale : (bool) 1889 enable scaling of the widget 1890 initial_bounds : (list) 1891 initial bounds of the box widget 1892 c : (color) 1893 color of the box cutter widget 1894 alpha : (float) 1895 transparency of the cut-off part of the input mesh 1896 """ 1897 super().__init__() 1898 1899 self.mesh = mesh 1900 self.remnant = Mesh() 1901 self.remnant.name = mesh.name + "Remnant" 1902 self.remnant.pickable(False) 1903 1904 self._alpha = alpha 1905 self._keypress_id = None 1906 self._init_bounds = initial_bounds 1907 if len(self._init_bounds) == 0: 1908 self._init_bounds = mesh.bounds() 1909 else: 1910 self._init_bounds = initial_bounds 1911 1912 self._implicit_func = vtk.vtkPlanes() 1913 self._implicit_func.SetBounds(self._init_bounds) 1914 1915 poly = mesh.polydata() 1916 self.clipper = vtk.vtkClipPolyData() 1917 self.clipper.GenerateClipScalarsOff() 1918 self.clipper.SetInputData(poly) 1919 self.clipper.SetClipFunction(self._implicit_func) 1920 self.clipper.SetInsideOut(not invert) 1921 self.clipper.GenerateClippedOutputOn() 1922 self.clipper.Update() 1923 1924 self.widget = vtk.vtkBoxWidget() 1925 1926 self.widget.SetRotationEnabled(can_rotate) 1927 self.widget.SetTranslationEnabled(can_translate) 1928 self.widget.SetScalingEnabled(can_scale) 1929 1930 self.widget.OutlineCursorWiresOn() 1931 self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3")) 1932 self.widget.GetSelectedHandleProperty().SetColor(get_color("red5")) 1933 1934 self.widget.GetOutlineProperty().SetColor(c) 1935 self.widget.GetOutlineProperty().SetOpacity(1) 1936 self.widget.GetOutlineProperty().SetLineWidth(1) 1937 self.widget.GetOutlineProperty().LightingOff() 1938 1939 self.widget.GetSelectedFaceProperty().LightingOff() 1940 self.widget.GetSelectedFaceProperty().SetOpacity(0.1) 1941 1942 self.widget.SetPlaceFactor(1.0 + padding) 1943 self.widget.SetInputData(poly) 1944 self.widget.PlaceWidget() 1945 self.widget.AddObserver("InteractionEvent", self._select_polygons) 1946 1947 def _select_polygons(self, vobj, event): 1948 vobj.GetPlanes(self._implicit_func) 1949 1950 def _keypress(self, vobj, event): 1951 if vobj.GetKeySym() == "r": # reset planes 1952 self._implicit_func.SetBounds(self._init_bounds) 1953 self.widget.GetPlanes(self._implicit_func) 1954 self.widget.PlaceWidget() 1955 self.widget.GetInteractor().Render() 1956 elif vobj.GetKeySym() == "u": 1957 self.invert() 1958 self.widget.GetInteractor().Render() 1959 elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh 1960 if self.widget.GetInteractor(): 1961 if self.widget.GetInteractor().GetControlKey(): 1962 printc(":save: saving mesh to vedo_clipped.vtk") 1963 self.mesh.write("vedo_clipped.vtk")
Create a box widget to cut away parts of a Mesh.
1864 def __init__( 1865 self, 1866 mesh, 1867 invert=False, 1868 can_rotate=True, 1869 can_translate=True, 1870 can_scale=True, 1871 initial_bounds=(), 1872 padding=0.025, 1873 c=(0.25, 0.25, 0.25), 1874 alpha=0.05, 1875 ): 1876 """ 1877 Create a box widget to cut away parts of a Mesh. 1878 1879 Arguments: 1880 mesh : (Mesh) 1881 the input mesh 1882 invert : (bool) 1883 invert the clipping plane 1884 can_rotate : (bool) 1885 enable rotation of the widget 1886 can_translate : (bool) 1887 enable translation of the widget 1888 can_scale : (bool) 1889 enable scaling of the widget 1890 initial_bounds : (list) 1891 initial bounds of the box widget 1892 c : (color) 1893 color of the box cutter widget 1894 alpha : (float) 1895 transparency of the cut-off part of the input mesh 1896 """ 1897 super().__init__() 1898 1899 self.mesh = mesh 1900 self.remnant = Mesh() 1901 self.remnant.name = mesh.name + "Remnant" 1902 self.remnant.pickable(False) 1903 1904 self._alpha = alpha 1905 self._keypress_id = None 1906 self._init_bounds = initial_bounds 1907 if len(self._init_bounds) == 0: 1908 self._init_bounds = mesh.bounds() 1909 else: 1910 self._init_bounds = initial_bounds 1911 1912 self._implicit_func = vtk.vtkPlanes() 1913 self._implicit_func.SetBounds(self._init_bounds) 1914 1915 poly = mesh.polydata() 1916 self.clipper = vtk.vtkClipPolyData() 1917 self.clipper.GenerateClipScalarsOff() 1918 self.clipper.SetInputData(poly) 1919 self.clipper.SetClipFunction(self._implicit_func) 1920 self.clipper.SetInsideOut(not invert) 1921 self.clipper.GenerateClippedOutputOn() 1922 self.clipper.Update() 1923 1924 self.widget = vtk.vtkBoxWidget() 1925 1926 self.widget.SetRotationEnabled(can_rotate) 1927 self.widget.SetTranslationEnabled(can_translate) 1928 self.widget.SetScalingEnabled(can_scale) 1929 1930 self.widget.OutlineCursorWiresOn() 1931 self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3")) 1932 self.widget.GetSelectedHandleProperty().SetColor(get_color("red5")) 1933 1934 self.widget.GetOutlineProperty().SetColor(c) 1935 self.widget.GetOutlineProperty().SetOpacity(1) 1936 self.widget.GetOutlineProperty().SetLineWidth(1) 1937 self.widget.GetOutlineProperty().LightingOff() 1938 1939 self.widget.GetSelectedFaceProperty().LightingOff() 1940 self.widget.GetSelectedFaceProperty().SetOpacity(0.1) 1941 1942 self.widget.SetPlaceFactor(1.0 + padding) 1943 self.widget.SetInputData(poly) 1944 self.widget.PlaceWidget() 1945 self.widget.AddObserver("InteractionEvent", self._select_polygons)
Create a box widget to cut away parts of a Mesh.
Arguments:
- mesh : (Mesh) the input mesh
- invert : (bool) invert the clipping plane
- can_rotate : (bool) enable rotation of the widget
- can_translate : (bool) enable translation of the widget
- can_scale : (bool) enable scaling of the widget
- initial_bounds : (list) initial bounds of the box widget
- c : (color) color of the box cutter widget
- alpha : (float) transparency of the cut-off part of the input mesh
Inherited Members
1728class PlaneCutter(vtk.vtkPlaneWidget, BaseCutter): 1729 """ 1730 Create a box widget to cut away parts of a Mesh. 1731 """ 1732 def __init__( 1733 self, 1734 mesh, 1735 invert=False, 1736 can_translate=True, 1737 can_scale=True, 1738 c=(0.25, 0.25, 0.25), 1739 origin=(), 1740 normal=(), 1741 padding=0.05, 1742 alpha=0.05, 1743 ): 1744 """ 1745 Create a box widget to cut away parts of a Mesh. 1746 1747 Arguments: 1748 mesh : (Mesh) 1749 the input mesh 1750 invert : (bool) 1751 invert the clipping plane 1752 can_translate : (bool) 1753 enable translation of the widget 1754 can_scale : (bool) 1755 enable scaling of the widget 1756 origin : (list) 1757 origin of the plane 1758 normal : (list) 1759 normal to the plane 1760 padding : (float) 1761 padding around the input mesh 1762 c : (color) 1763 color of the box cutter widget 1764 alpha : (float) 1765 transparency of the cut-off part of the input mesh 1766 """ 1767 super().__init__() 1768 1769 self.mesh = mesh 1770 self.remnant = Mesh() 1771 self.remnant.name = mesh.name + "Remnant" 1772 self.remnant.pickable(False) 1773 1774 self._alpha = alpha 1775 self._keypress_id = None 1776 1777 self._implicit_func = vtk.vtkPlane() 1778 1779 poly = mesh.polydata() 1780 self.clipper = vtk.vtkClipPolyData() 1781 self.clipper.GenerateClipScalarsOff() 1782 self.clipper.SetInputData(poly) 1783 self.clipper.SetClipFunction(self._implicit_func) 1784 self.clipper.SetInsideOut(invert) 1785 self.clipper.GenerateClippedOutputOn() 1786 self.clipper.Update() 1787 1788 self.widget = vtk.vtkImplicitPlaneWidget() 1789 1790 # self.widget.KeyPressActivationOff() 1791 # self.widget.SetKeyPressActivationValue('i') 1792 1793 self.widget.SetOriginTranslation(can_translate) 1794 self.widget.SetOutlineTranslation(can_translate) 1795 self.widget.SetScaleEnabled(can_scale) 1796 1797 self.widget.GetOutlineProperty().SetColor(c) 1798 self.widget.GetOutlineProperty().SetOpacity(0.25) 1799 self.widget.GetOutlineProperty().SetLineWidth(1) 1800 self.widget.GetOutlineProperty().LightingOff() 1801 1802 self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3")) 1803 1804 self.widget.SetTubing(0) 1805 self.widget.SetDrawPlane(1) 1806 self.widget.GetPlaneProperty().LightingOff() 1807 self.widget.GetPlaneProperty().SetOpacity(0.05) 1808 self.widget.GetSelectedPlaneProperty().SetColor(get_color("red5")) 1809 self.widget.GetSelectedPlaneProperty().LightingOff() 1810 1811 self.widget.SetPlaceFactor(1.0 + padding) 1812 self.widget.SetInputData(poly) 1813 self.widget.PlaceWidget() 1814 self.widget.AddObserver("InteractionEvent", self._select_polygons) 1815 1816 if len(origin) == 3: 1817 self.widget.SetOrigin(origin) 1818 else: 1819 self.widget.SetOrigin(mesh.center_of_mass()) 1820 1821 if len(normal) == 3: 1822 self.widget.SetNormal(normal) 1823 else: 1824 self.widget.SetNormal((1, 0, 0)) 1825 1826 1827 def _select_polygons(self, vobj, event): 1828 vobj.GetPlane(self._implicit_func) 1829 1830 def _keypress(self, vobj, event): 1831 if vobj.GetKeySym() == "r": # reset planes 1832 self.widget.GetPlane(self._implicit_func) 1833 self.widget.PlaceWidget() 1834 self.widget.GetInteractor().Render() 1835 elif vobj.GetKeySym() == "u": # invert cut 1836 self.invert() 1837 self.widget.GetInteractor().Render() 1838 elif vobj.GetKeySym() == "x": # set normal along x 1839 self.widget.SetNormal((1, 0, 0)) 1840 self.widget.GetPlane(self._implicit_func) 1841 self.widget.PlaceWidget() 1842 self.widget.GetInteractor().Render() 1843 elif vobj.GetKeySym() == "y": # set normal along y 1844 self.widget.SetNormal((0, 1, 0)) 1845 self.widget.GetPlane(self._implicit_func) 1846 self.widget.PlaceWidget() 1847 self.widget.GetInteractor().Render() 1848 elif vobj.GetKeySym() == "z": # set normal along z 1849 self.widget.SetNormal((0, 0, 1)) 1850 self.widget.GetPlane(self._implicit_func) 1851 self.widget.PlaceWidget() 1852 self.widget.GetInteractor().Render() 1853 elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh 1854 if self.widget.GetInteractor(): 1855 if self.widget.GetInteractor().GetControlKey(): 1856 printc(":save: saving mesh to vedo_clipped.vtk") 1857 self.mesh.write("vedo_clipped.vtk")
Create a box widget to cut away parts of a Mesh.
1732 def __init__( 1733 self, 1734 mesh, 1735 invert=False, 1736 can_translate=True, 1737 can_scale=True, 1738 c=(0.25, 0.25, 0.25), 1739 origin=(), 1740 normal=(), 1741 padding=0.05, 1742 alpha=0.05, 1743 ): 1744 """ 1745 Create a box widget to cut away parts of a Mesh. 1746 1747 Arguments: 1748 mesh : (Mesh) 1749 the input mesh 1750 invert : (bool) 1751 invert the clipping plane 1752 can_translate : (bool) 1753 enable translation of the widget 1754 can_scale : (bool) 1755 enable scaling of the widget 1756 origin : (list) 1757 origin of the plane 1758 normal : (list) 1759 normal to the plane 1760 padding : (float) 1761 padding around the input mesh 1762 c : (color) 1763 color of the box cutter widget 1764 alpha : (float) 1765 transparency of the cut-off part of the input mesh 1766 """ 1767 super().__init__() 1768 1769 self.mesh = mesh 1770 self.remnant = Mesh() 1771 self.remnant.name = mesh.name + "Remnant" 1772 self.remnant.pickable(False) 1773 1774 self._alpha = alpha 1775 self._keypress_id = None 1776 1777 self._implicit_func = vtk.vtkPlane() 1778 1779 poly = mesh.polydata() 1780 self.clipper = vtk.vtkClipPolyData() 1781 self.clipper.GenerateClipScalarsOff() 1782 self.clipper.SetInputData(poly) 1783 self.clipper.SetClipFunction(self._implicit_func) 1784 self.clipper.SetInsideOut(invert) 1785 self.clipper.GenerateClippedOutputOn() 1786 self.clipper.Update() 1787 1788 self.widget = vtk.vtkImplicitPlaneWidget() 1789 1790 # self.widget.KeyPressActivationOff() 1791 # self.widget.SetKeyPressActivationValue('i') 1792 1793 self.widget.SetOriginTranslation(can_translate) 1794 self.widget.SetOutlineTranslation(can_translate) 1795 self.widget.SetScaleEnabled(can_scale) 1796 1797 self.widget.GetOutlineProperty().SetColor(c) 1798 self.widget.GetOutlineProperty().SetOpacity(0.25) 1799 self.widget.GetOutlineProperty().SetLineWidth(1) 1800 self.widget.GetOutlineProperty().LightingOff() 1801 1802 self.widget.GetSelectedOutlineProperty().SetColor(get_color("red3")) 1803 1804 self.widget.SetTubing(0) 1805 self.widget.SetDrawPlane(1) 1806 self.widget.GetPlaneProperty().LightingOff() 1807 self.widget.GetPlaneProperty().SetOpacity(0.05) 1808 self.widget.GetSelectedPlaneProperty().SetColor(get_color("red5")) 1809 self.widget.GetSelectedPlaneProperty().LightingOff() 1810 1811 self.widget.SetPlaceFactor(1.0 + padding) 1812 self.widget.SetInputData(poly) 1813 self.widget.PlaceWidget() 1814 self.widget.AddObserver("InteractionEvent", self._select_polygons) 1815 1816 if len(origin) == 3: 1817 self.widget.SetOrigin(origin) 1818 else: 1819 self.widget.SetOrigin(mesh.center_of_mass()) 1820 1821 if len(normal) == 3: 1822 self.widget.SetNormal(normal) 1823 else: 1824 self.widget.SetNormal((1, 0, 0))
Create a box widget to cut away parts of a Mesh.
Arguments:
- mesh : (Mesh) the input mesh
- invert : (bool) invert the clipping plane
- can_translate : (bool) enable translation of the widget
- can_scale : (bool) enable scaling of the widget
- origin : (list) origin of the plane
- normal : (list) normal to the plane
- padding : (float) padding around the input mesh
- c : (color) color of the box cutter widget
- alpha : (float) transparency of the cut-off part of the input mesh
Inherited Members
1966class SphereCutter(vtk.vtkSphereWidget, BaseCutter): 1967 """ 1968 Create a box widget to cut away parts of a Mesh. 1969 """ 1970 def __init__( 1971 self, 1972 mesh, 1973 invert=False, 1974 can_translate=True, 1975 can_scale=True, 1976 origin=(), 1977 radius=0, 1978 res=60, 1979 c='white', 1980 alpha=0.05, 1981 ): 1982 """ 1983 Create a box widget to cut away parts of a Mesh. 1984 1985 Arguments: 1986 mesh : Mesh 1987 the input mesh 1988 invert : bool 1989 invert the clipping 1990 can_translate : bool 1991 enable translation of the widget 1992 can_scale : bool 1993 enable scaling of the widget 1994 origin : list 1995 initial position of the sphere widget 1996 radius : float 1997 initial radius of the sphere widget 1998 res : int 1999 resolution of the sphere widget 2000 c : color 2001 color of the box cutter widget 2002 alpha : float 2003 transparency of the cut-off part of the input mesh 2004 """ 2005 super().__init__() 2006 2007 self.mesh = mesh 2008 self.remnant = Mesh() 2009 self.remnant.name = mesh.name + "Remnant" 2010 self.remnant.pickable(False) 2011 2012 self._alpha = alpha 2013 self._keypress_id = None 2014 2015 self._implicit_func = vtk.vtkSphere() 2016 2017 if len(origin) == 3: 2018 self._implicit_func.SetCenter(origin) 2019 else: 2020 origin = mesh.center_of_mass() 2021 self._implicit_func.SetCenter(origin) 2022 2023 if radius > 0: 2024 self._implicit_func.SetRadius(radius) 2025 else: 2026 radius = mesh.average_size() * 2 2027 self._implicit_func.SetRadius(radius) 2028 2029 poly = mesh.polydata() 2030 self.clipper = vtk.vtkClipPolyData() 2031 self.clipper.GenerateClipScalarsOff() 2032 self.clipper.SetInputData(poly) 2033 self.clipper.SetClipFunction(self._implicit_func) 2034 self.clipper.SetInsideOut(not invert) 2035 self.clipper.GenerateClippedOutputOn() 2036 self.clipper.Update() 2037 2038 self.widget = vtk.vtkSphereWidget() 2039 2040 self.widget.SetThetaResolution(res*2) 2041 self.widget.SetPhiResolution(res) 2042 self.widget.SetRadius(radius) 2043 self.widget.SetCenter(origin) 2044 self.widget.SetRepresentation(2) 2045 self.widget.HandleVisibilityOff() 2046 2047 self.widget.SetTranslation(can_translate) 2048 self.widget.SetScale(can_scale) 2049 2050 self.widget.HandleVisibilityOff() 2051 self.widget.GetSphereProperty().SetColor(get_color(c)) 2052 self.widget.GetSphereProperty().SetOpacity(0.2) 2053 self.widget.GetSelectedSphereProperty().SetColor(get_color("red5")) 2054 self.widget.GetSelectedSphereProperty().SetOpacity(0.2) 2055 2056 self.widget.SetPlaceFactor(1.0) 2057 self.widget.SetInputData(poly) 2058 self.widget.PlaceWidget() 2059 self.widget.AddObserver("InteractionEvent", self._select_polygons) 2060 2061 def _select_polygons(self, vobj, event): 2062 vobj.GetSphere(self._implicit_func) 2063 2064 def _keypress(self, vobj, event): 2065 if vobj.GetKeySym() == "r": # reset planes 2066 self._implicit_func.SetBounds(self._init_bounds) 2067 self.widget.GetPlanes(self._implicit_func) 2068 self.widget.PlaceWidget() 2069 self.widget.GetInteractor().Render() 2070 elif vobj.GetKeySym() == "u": 2071 self.invert() 2072 self.widget.GetInteractor().Render() 2073 elif vobj.GetKeySym() == "s": # Ctrl+s to save mesh 2074 if self.widget.GetInteractor(): 2075 if self.widget.GetInteractor().GetControlKey(): 2076 printc(":save: saving mesh to vedo_clipped.vtk") 2077 self.mesh.write("vedo_clipped.vtk")
Create a box widget to cut away parts of a Mesh.
1970 def __init__( 1971 self, 1972 mesh, 1973 invert=False, 1974 can_translate=True, 1975 can_scale=True, 1976 origin=(), 1977 radius=0, 1978 res=60, 1979 c='white', 1980 alpha=0.05, 1981 ): 1982 """ 1983 Create a box widget to cut away parts of a Mesh. 1984 1985 Arguments: 1986 mesh : Mesh 1987 the input mesh 1988 invert : bool 1989 invert the clipping 1990 can_translate : bool 1991 enable translation of the widget 1992 can_scale : bool 1993 enable scaling of the widget 1994 origin : list 1995 initial position of the sphere widget 1996 radius : float 1997 initial radius of the sphere widget 1998 res : int 1999 resolution of the sphere widget 2000 c : color 2001 color of the box cutter widget 2002 alpha : float 2003 transparency of the cut-off part of the input mesh 2004 """ 2005 super().__init__() 2006 2007 self.mesh = mesh 2008 self.remnant = Mesh() 2009 self.remnant.name = mesh.name + "Remnant" 2010 self.remnant.pickable(False) 2011 2012 self._alpha = alpha 2013 self._keypress_id = None 2014 2015 self._implicit_func = vtk.vtkSphere() 2016 2017 if len(origin) == 3: 2018 self._implicit_func.SetCenter(origin) 2019 else: 2020 origin = mesh.center_of_mass() 2021 self._implicit_func.SetCenter(origin) 2022 2023 if radius > 0: 2024 self._implicit_func.SetRadius(radius) 2025 else: 2026 radius = mesh.average_size() * 2 2027 self._implicit_func.SetRadius(radius) 2028 2029 poly = mesh.polydata() 2030 self.clipper = vtk.vtkClipPolyData() 2031 self.clipper.GenerateClipScalarsOff() 2032 self.clipper.SetInputData(poly) 2033 self.clipper.SetClipFunction(self._implicit_func) 2034 self.clipper.SetInsideOut(not invert) 2035 self.clipper.GenerateClippedOutputOn() 2036 self.clipper.Update() 2037 2038 self.widget = vtk.vtkSphereWidget() 2039 2040 self.widget.SetThetaResolution(res*2) 2041 self.widget.SetPhiResolution(res) 2042 self.widget.SetRadius(radius) 2043 self.widget.SetCenter(origin) 2044 self.widget.SetRepresentation(2) 2045 self.widget.HandleVisibilityOff() 2046 2047 self.widget.SetTranslation(can_translate) 2048 self.widget.SetScale(can_scale) 2049 2050 self.widget.HandleVisibilityOff() 2051 self.widget.GetSphereProperty().SetColor(get_color(c)) 2052 self.widget.GetSphereProperty().SetOpacity(0.2) 2053 self.widget.GetSelectedSphereProperty().SetColor(get_color("red5")) 2054 self.widget.GetSelectedSphereProperty().SetOpacity(0.2) 2055 2056 self.widget.SetPlaceFactor(1.0) 2057 self.widget.SetInputData(poly) 2058 self.widget.PlaceWidget() 2059 self.widget.AddObserver("InteractionEvent", self._select_polygons)
Create a box widget to cut away parts of a Mesh.
Arguments:
- mesh : Mesh the input mesh
- invert : bool invert the clipping
- can_translate : bool enable translation of the widget
- can_scale : bool enable scaling of the widget
- origin : list initial position of the sphere widget
- radius : float initial radius of the sphere widget
- res : int resolution of the sphere widget
- c : color color of the box cutter widget
- alpha : float transparency of the cut-off part of the input mesh