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