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