vedo.utils
Utilities submodule.
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3import math 4import os 5import time 6 7import numpy as np 8 9try: 10 import vedo.vtkclasses as vtk 11except ImportError: 12 import vtkmodules.all as vtk 13 14from vtkmodules.util.numpy_support import numpy_to_vtk 15from vtkmodules.util.numpy_support import numpy_to_vtkIdTypeArray 16from vtkmodules.util.numpy_support import vtk_to_numpy 17 18import vedo 19 20 21__docformat__ = "google" 22 23__doc__ = "Utilities submodule." 24 25__all__ = [ 26 "OperationNode", 27 "ProgressBar", 28 "progressbar", 29 "geometry", 30 "extract_cells_by_type", 31 "is_sequence", 32 "lin_interpolate", 33 "vector", 34 "mag", 35 "mag2", 36 "versor", 37 "precision", 38 "round_to_digit", 39 "point_in_triangle", 40 "point_line_distance", 41 "closest", 42 "grep", 43 "print_info", 44 "make_bands", 45 "pack_spheres", 46 "spher2cart", 47 "cart2spher", 48 "cart2cyl", 49 "cyl2cart", 50 "cyl2spher", 51 "spher2cyl", 52 "cart2pol", 53 "pol2cart", 54 "humansort", 55 "print_histogram", 56 "camera_from_quaternion", 57 "camera_from_neuroglancer", 58 "oriented_camera", 59 "vedo2trimesh", 60 "trimesh2vedo", 61 "vedo2meshlab", 62 "meshlab2vedo", 63 "vedo2open3d", 64 "open3d2vedo", 65 "vtk2numpy", 66 "numpy2vtk", 67 "get_uv", 68] 69 70 71########################################################################### 72array_types = {} 73array_types[vtk.VTK_UNSIGNED_CHAR] = ("UNSIGNED_CHAR", "np.uint8") 74array_types[vtk.VTK_UNSIGNED_SHORT]= ("UNSIGNED_SHORT", "np.uint16") 75array_types[vtk.VTK_UNSIGNED_INT] = ("UNSIGNED_INT", "np.uint32") 76array_types[vtk.VTK_UNSIGNED_LONG_LONG] = ("UNSIGNED_LONG_LONG", "np.uint64") 77array_types[vtk.VTK_CHAR] = ("CHAR", "np.int8") 78array_types[vtk.VTK_SHORT] = ("SHORT", "np.int16") 79array_types[vtk.VTK_INT] = ("INT", "np.int32") 80array_types[vtk.VTK_LONG] = ("LONG", "") # ?? 81array_types[vtk.VTK_LONG_LONG] = ("LONG_LONG", "np.int64") 82array_types[vtk.VTK_FLOAT] = ("FLOAT", "np.float32") 83array_types[vtk.VTK_DOUBLE] = ("DOUBLE", "np.float64") 84array_types[vtk.VTK_SIGNED_CHAR] = ("SIGNED_CHAR", "np.int8") 85array_types[vtk.VTK_ID_TYPE] = ("ID", "np.int64") 86 87 88########################################################################### 89class OperationNode: 90 """ 91 Keep track of the operations which led to a final object. 92 """ 93 # https://www.graphviz.org/doc/info/shapes.html#html 94 # Mesh #e9c46a 95 # Follower #d9ed92 96 # Volume, UGrid #4cc9f0 97 # TetMesh #9e2a2b 98 # File #8a817c 99 # Picture #f28482 100 # Assembly #f08080 101 102 def __init__( 103 self, operation, parents=(), comment="", shape="none", c="#e9c46a", style="filled" 104 ): 105 """ 106 Keep track of the operations which led to a final object. 107 This allows to show the `pipeline` tree for any `vedo` object with e.g.: 108 109 ```python 110 from vedo import * 111 sp = Sphere() 112 sp.clean().subdivide() 113 sp.pipeline.show() 114 ``` 115 116 Arguments: 117 operation : (str, class) 118 descriptor label, if a class is passed then grab its name 119 parents : (list) 120 list of the parent classes the object comes from 121 comment : (str) 122 a second-line text description 123 shape : (str) 124 shape of the frame, check out [this link.](https://graphviz.org/doc/info/shapes.html) 125 c : (hex) 126 hex color 127 style : (str) 128 comma-separated list of styles 129 130 Example: 131 ```python 132 from vedo.utils import OperationNode 133 134 op_node1 = OperationNode("Operation1", c="lightblue") 135 op_node2 = OperationNode("Operation2") 136 op_node3 = OperationNode("Operation3", shape='diamond') 137 op_node4 = OperationNode("Operation4") 138 op_node5 = OperationNode("Operation5") 139 op_node6 = OperationNode("Result", c="lightgreen") 140 141 op_node3.add_parent(op_node1) 142 op_node4.add_parent(op_node1) 143 op_node3.add_parent(op_node2) 144 op_node5.add_parent(op_node2) 145 op_node6.add_parent(op_node3) 146 op_node6.add_parent(op_node5) 147 op_node6.add_parent(op_node1) 148 149 op_node6.show(orientation="TB") 150 ``` 151 ![](https://vedo.embl.es/images/feats/operation_node.png) 152 """ 153 if not vedo.settings.enable_pipeline: 154 return 155 156 if isinstance(operation, str): 157 self.operation = operation 158 else: 159 self.operation = operation.__class__.__name__ 160 self.operation_plain = str(self.operation) 161 162 pp = [] # filter out invalid stuff 163 for p in parents: 164 if hasattr(p, "pipeline"): 165 pp.append(p.pipeline) 166 self.parents = pp 167 168 if comment: 169 self.operation = f"<{self.operation}<BR/><SUB><I>{comment}</I></SUB>>" 170 171 self.dot = None 172 self.time = time.time() 173 self.shape = shape 174 self.style = style 175 self.color = c 176 self.counts = 0 177 178 def add_parent(self, parent): 179 self.parents.append(parent) 180 181 def _build_tree(self, dot): 182 dot.node( 183 str(id(self)), 184 label=self.operation, 185 shape=self.shape, 186 color=self.color, 187 style=self.style, 188 ) 189 for parent in self.parents: 190 if parent: 191 t = f"{self.time - parent.time: .1f}s" 192 dot.edge(str(id(parent)), str(id(self)), label=t) 193 parent._build_tree(dot) 194 195 def __repr__(self): 196 try: 197 from treelib import Tree 198 except ImportError: 199 vedo.logger.error( 200 "To use this functionality please install treelib:" "\n pip install treelib" 201 ) 202 return "" 203 204 def _build_tree(parent): 205 for par in parent.parents: 206 if par: 207 op = par.operation_plain 208 tree.create_node( 209 op, op + str(par.time), parent=parent.operation_plain + str(parent.time) 210 ) 211 _build_tree(par) 212 213 tree = Tree() 214 tree.create_node(self.operation_plain, self.operation_plain + str(self.time)) 215 _build_tree(self) 216 return tree.show(reverse=True, stdout=False) 217 218 def show(self, orientation="LR", popup=True): 219 """Show the graphviz output for the pipeline of this object""" 220 if not vedo.settings.enable_pipeline: 221 return 222 223 try: 224 from graphviz import Digraph 225 except ImportError: 226 vedo.logger.error("please install graphviz with command\n pip install graphviz") 227 return 228 229 # visualize the entire tree 230 dot = Digraph( 231 node_attr={"fontcolor": "#201010", "fontname": "Helvetica", "fontsize": "12"}, 232 edge_attr={"fontname": "Helvetica", "fontsize": "6", "arrowsize": "0.4"}, 233 ) 234 dot.attr(rankdir=orientation) 235 236 self.counts = 0 237 self._build_tree(dot) 238 self.dot = dot 239 dot.render(".vedo_pipeline_graphviz", view=popup) 240 241 242########################################################################### 243class ProgressBar: 244 """ 245 Class to print a progress bar. 246 """ 247 248 def __init__( 249 self, 250 start, 251 stop, 252 step=1, 253 c=None, 254 bold=True, 255 italic=False, 256 title="", 257 eta=True, 258 delay=0.0, 259 width=25, 260 char="\U00002501", 261 char_back="\U00002500", 262 ): 263 """ 264 Class to print a progress bar with optional text message. 265 266 Check out also function `progressbar()`. 267 268 Example: 269 ```python 270 import time 271 pb = ProgressBar(0,400, c='red') 272 for i in pb.range(): 273 time.sleep(0.1) 274 pb.print('some message') 275 ``` 276 ![](https://user-images.githubusercontent.com/32848391/51858823-ed1f4880-2335-11e9-8788-2d102ace2578.png) 277 """ 278 self.char = char 279 self.char_back = char_back 280 281 self.title = title + " " 282 if title: 283 self.title = " " + self.title 284 285 self.start = start 286 self.stop = stop 287 self.step = step 288 289 self.color = c 290 self.bold = bold 291 self.italic = italic 292 self.width = width 293 self.pbar = "" 294 self.percent = 0.0 295 self.percent_int = 0 296 self.eta = eta 297 self.delay = delay 298 299 self.t0 = time.time() 300 self._remaining = 1e10 301 302 self._update(0) 303 304 self._counts = 0 305 self._oldbar = "" 306 self._lentxt = 0 307 self._range = np.arange(start, stop, step) 308 309 def print(self, txt="", c=None): 310 """Print the progress bar with an optional message.""" 311 if not c: 312 c = self.color 313 314 self._update(self._counts + self.step) 315 316 if self.delay: 317 if time.time() - self.t0 < self.delay: 318 return 319 320 if self.pbar != self._oldbar: 321 self._oldbar = self.pbar 322 323 if self.eta and self._counts > 1: 324 325 tdenom = time.time() - self.t0 326 if tdenom: 327 vel = self._counts / tdenom 328 self._remaining = (self.stop - self._counts) / vel 329 else: 330 vel = 1 331 self._remaining = 0.0 332 333 if self._remaining > 60: 334 mins = int(self._remaining / 60) 335 secs = self._remaining - 60 * mins 336 mins = f"{mins}m" 337 secs = f"{int(secs + 0.5)}s " 338 else: 339 mins = "" 340 secs = f"{int(self._remaining + 0.5)}s " 341 342 vel = round(vel, 1) 343 eta = f"eta: {mins}{secs}({vel} it/s) " 344 if self._remaining < 0.5: 345 dt = time.time() - self.t0 346 if dt > 60: 347 mins = int(dt / 60) 348 secs = dt - 60 * mins 349 mins = f"{mins}m" 350 secs = f"{int(secs + 0.5)}s " 351 else: 352 mins = "" 353 secs = f"{int(dt + 0.5)}s " 354 eta = f"elapsed: {mins}{secs}({vel} it/s) " 355 txt = "" 356 else: 357 eta = "" 358 359 eraser = " " * self._lentxt + "\b" * self._lentxt 360 361 s = f"{self.pbar} {eraser}{eta}{txt}\r" 362 vedo.printc(s, c=c, bold=self.bold, italic=self.italic, end="") 363 if self.percent > 99.999: 364 print("") 365 366 self._lentxt = len(txt) 367 368 def range(self): 369 """Return the range iterator.""" 370 return self._range 371 372 def _update(self, counts): 373 if counts < self.start: 374 counts = self.start 375 elif counts > self.stop: 376 counts = self.stop 377 self._counts = counts 378 379 self.percent = (self._counts - self.start) * 100.0 380 381 delta = self.stop - self.start 382 if delta: 383 self.percent /= delta 384 else: 385 self.percent = 0.0 386 387 self.percent_int = int(round(self.percent)) 388 af = self.width - 2 389 nh = int(round(self.percent_int / 100 * af)) 390 pbar_background = "\x1b[2m" + self.char_back * (af - nh) 391 self.pbar = f"{self.title}{self.char * (nh-1)}{pbar_background}" 392 if self.percent < 100.0: 393 ps = f" {self.percent_int}%" 394 else: 395 ps = "" 396 self.pbar += ps 397 398 399##################################### 400def progressbar(iterable, c=None, bold=True, italic=False, title="", eta=True, width=25, delay=0.5): 401 """ 402 Function to print a progress bar with optional text message. 403 404 Use delay to set a minimum time before printing anything. 405 406 Example: 407 ```python 408 import time 409 for i in progressbar(range(100), c='red'): 410 time.sleep(0.1) 411 ``` 412 ![](https://user-images.githubusercontent.com/32848391/51858823-ed1f4880-2335-11e9-8788-2d102ace2578.png) 413 """ 414 try: 415 if is_number(iterable): 416 total = int(iterable) 417 iterable = range(total) 418 else: 419 total = len(iterable) 420 except TypeError: 421 iterable = list(iterable) 422 total = len(iterable) 423 424 pb = ProgressBar( 425 0, total, c=c, bold=bold, italic=italic, title=title, eta=eta, delay=delay, width=width 426 ) 427 for item in iterable: 428 pb.print() 429 yield item 430 431 432########################################################### 433def numpy2vtk(arr, dtype=None, deep=True, name=""): 434 """ 435 Convert a numpy array into a `vtkDataArray`. 436 Use `dtype='id'` for `vtkIdTypeArray` objects. 437 """ 438 # https://github.com/Kitware/VTK/blob/master/Wrapping/Python/vtkmodules/util/numpy_support.py 439 if arr is None: 440 return None 441 442 arr = np.ascontiguousarray(arr) 443 444 if dtype == "id": 445 varr = numpy_to_vtkIdTypeArray(arr.astype(np.int64), deep=deep) 446 elif dtype: 447 varr = numpy_to_vtk(arr.astype(dtype), deep=deep) 448 else: 449 # let numpy_to_vtk() decide what is best type based on arr type 450 varr = numpy_to_vtk(arr, deep=deep) 451 452 if name: 453 varr.SetName(name) 454 return varr 455 456 457def vtk2numpy(varr): 458 """Convert a `vtkDataArray`, `vtkIdList` or `vtTransform` into a numpy array.""" 459 if isinstance(varr, vtk.vtkIdList): 460 return np.array([varr.GetId(i) for i in range(varr.GetNumberOfIds())]) 461 elif isinstance(varr, vtk.vtkBitArray): 462 carr = vtk.vtkCharArray() 463 carr.DeepCopy(varr) 464 varr = carr 465 elif isinstance(varr, vtk.vtkHomogeneousTransform): 466 try: 467 varr = varr.GetMatrix() 468 except AttributeError: 469 pass 470 n = 4 471 M = [[varr.GetElement(i, j) for j in range(n)] for i in range(n)] 472 return np.array(M) 473 474 return vtk_to_numpy(varr) 475 476 477def make3d(pts, transpose=False): 478 """ 479 Make an array which might be 2D to 3D. 480 481 Array can also be in the form `[allx, ally, allz]`. 482 Use `transpose` to resolve ambiguous cases (eg, shapes like `[3,3]`). 483 """ 484 pts = np.asarray(pts) 485 486 if pts.dtype == "object": 487 raise ValueError("Cannot form a valid numpy array, input may be non-homogenous") 488 489 if pts.shape[0] == 0: # empty list 490 return pts 491 492 if pts.ndim == 1: 493 if pts.shape[0] == 2: 494 return np.hstack([pts, [0]]).astype(pts.dtype) 495 elif pts.shape[0] == 3: 496 return pts 497 else: 498 raise ValueError 499 500 if pts.shape[1] == 3: 501 return pts 502 503 if transpose or (2 <= pts.shape[0] <= 3 and pts.shape[1] > 3): 504 pts = pts.T 505 506 if pts.shape[1] == 2: 507 return np.c_[pts, np.zeros(pts.shape[0], dtype=pts.dtype)] 508 509 if pts.shape[1] != 3: 510 raise ValueError("input shape is not supported.") 511 return pts 512 513 514def geometry(obj, extent=None): 515 """ 516 Apply the `vtkGeometryFilter` to the input object. 517 This is a general-purpose filter to extract geometry (and associated data) 518 from any type of dataset. 519 This filter also may be used to convert any type of data to polygonal type. 520 The conversion process may be less than satisfactory for some 3D datasets. 521 For example, this filter will extract the outer surface of a volume 522 or structured grid dataset. 523 524 Returns a `vedo.Mesh` object. 525 526 Set `extent` as the `[xmin,xmax, ymin,ymax, zmin,zmax]` bounding box to clip data. 527 """ 528 gf = vtk.vtkGeometryFilter() 529 gf.SetInputData(obj) 530 if extent is not None: 531 gf.SetExtent(extent) 532 gf.Update() 533 return vedo.Mesh(gf.GetOutput()) 534 535 536def extract_cells_by_type(obj, types=()): 537 """ 538 Extract cells of a specified type from a vtk dataset. 539 540 Given an input `vtkDataSet` and a list of cell types, produce an output 541 containing only cells of the specified type(s). 542 543 Find [here](https://vtk.org/doc/nightly/html/vtkCellType_8h_source.html) 544 the list of possible cell types. 545 546 Return: 547 a `vtkDataSet` object which can be wrapped. 548 """ 549 ef = vtk.vtkExtractCellsByType() 550 try: 551 ef.SetInputData(obj.inputdata()) 552 except: 553 ef.SetInputData(obj) 554 555 for ct in types: 556 ef.AddCellType(ct) 557 ef.Update() 558 return ef.GetOutput() 559 560 561def buildPolyData(vertices, faces=None, lines=None, index_offset=0, tetras=False): 562 """ 563 Build a `vtkPolyData` object from a list of vertices 564 where faces represents the connectivity of the polygonal mesh. 565 566 E.g. : 567 - `vertices=[[x1,y1,z1],[x2,y2,z2], ...]` 568 - `faces=[[0,1,2], [1,2,3], ...]` 569 - `lines=[[0,1], [1,2,3,4], ...]` 570 571 Use `index_offset=1` if face numbering starts from 1 instead of 0. 572 573 If `tetras=True`, interpret 4-point faces as tetrahedrons instead of surface quads. 574 """ 575 poly = vtk.vtkPolyData() 576 577 if len(vertices) == 0: 578 return poly 579 580 if not is_sequence(vertices[0]): 581 return poly 582 583 vertices = make3d(vertices) 584 585 source_points = vtk.vtkPoints() 586 source_points.SetData(numpy2vtk(vertices, dtype=np.float32)) 587 poly.SetPoints(source_points) 588 589 if lines is not None: 590 # Create a cell array to store the lines in and add the lines to it 591 linesarr = vtk.vtkCellArray() 592 if is_sequence(lines[0]): # assume format [(id0,id1),..] 593 for iline in lines: 594 for i in range(0, len(iline) - 1): 595 i1, i2 = iline[i], iline[i + 1] 596 if i1 != i2: 597 vline = vtk.vtkLine() 598 vline.GetPointIds().SetId(0, i1) 599 vline.GetPointIds().SetId(1, i2) 600 linesarr.InsertNextCell(vline) 601 else: # assume format [id0,id1,...] 602 for i in range(0, len(lines) - 1): 603 vline = vtk.vtkLine() 604 vline.GetPointIds().SetId(0, lines[i]) 605 vline.GetPointIds().SetId(1, lines[i + 1]) 606 linesarr.InsertNextCell(vline) 607 # print('Wrong format for lines in utils.buildPolydata(), skip.') 608 poly.SetLines(linesarr) 609 610 if faces is None: 611 source_vertices = vtk.vtkCellArray() 612 for i in range(len(vertices)): 613 source_vertices.InsertNextCell(1) 614 source_vertices.InsertCellPoint(i) 615 poly.SetVerts(source_vertices) 616 return poly ################### 617 618 # faces exist 619 source_polygons = vtk.vtkCellArray() 620 621 if isinstance(faces, np.ndarray) or not is_ragged(faces): 622 ##### all faces are composed of equal nr of vtxs, FAST 623 faces = np.asarray(faces) 624 ast = np.int32 625 if vtk.vtkIdTypeArray().GetDataTypeSize() != 4: 626 ast = np.int64 627 628 if faces.ndim > 1: 629 nf, nc = faces.shape 630 hs = np.hstack((np.zeros(nf)[:, None] + nc, faces)).astype(ast).ravel() 631 arr = numpy_to_vtkIdTypeArray(hs, deep=True) 632 source_polygons.SetCells(nf, arr) 633 634 else: 635 ############################# manually add faces, SLOW 636 637 showbar = False 638 if len(faces) > 25000: 639 showbar = True 640 pb = ProgressBar(0, len(faces), eta=False) 641 642 for f in faces: 643 n = len(f) 644 645 if n == 3: 646 ele = vtk.vtkTriangle() 647 pids = ele.GetPointIds() 648 for i in range(3): 649 pids.SetId(i, f[i] - index_offset) 650 source_polygons.InsertNextCell(ele) 651 652 elif n == 4 and tetras: 653 # do not use vtkTetra() because it fails 654 # with dolfin faces orientation 655 ele0 = vtk.vtkTriangle() 656 ele1 = vtk.vtkTriangle() 657 ele2 = vtk.vtkTriangle() 658 ele3 = vtk.vtkTriangle() 659 if index_offset: 660 for i in [0, 1, 2, 3]: 661 f[i] -= index_offset 662 f0, f1, f2, f3 = f 663 pid0 = ele0.GetPointIds() 664 pid1 = ele1.GetPointIds() 665 pid2 = ele2.GetPointIds() 666 pid3 = ele3.GetPointIds() 667 668 pid0.SetId(0, f0) 669 pid0.SetId(1, f1) 670 pid0.SetId(2, f2) 671 672 pid1.SetId(0, f0) 673 pid1.SetId(1, f1) 674 pid1.SetId(2, f3) 675 676 pid2.SetId(0, f1) 677 pid2.SetId(1, f2) 678 pid2.SetId(2, f3) 679 680 pid3.SetId(0, f2) 681 pid3.SetId(1, f3) 682 pid3.SetId(2, f0) 683 684 source_polygons.InsertNextCell(ele0) 685 source_polygons.InsertNextCell(ele1) 686 source_polygons.InsertNextCell(ele2) 687 source_polygons.InsertNextCell(ele3) 688 689 else: 690 ele = vtk.vtkPolygon() 691 pids = ele.GetPointIds() 692 pids.SetNumberOfIds(n) 693 for i in range(n): 694 pids.SetId(i, f[i] - index_offset) 695 source_polygons.InsertNextCell(ele) 696 if showbar: 697 pb.print("converting mesh... ") 698 699 poly.SetPolys(source_polygons) 700 return poly 701 702 703############################################################################## 704def get_font_path(font): 705 """Internal use.""" 706 if font in vedo.settings.font_parameters.keys(): 707 if vedo.settings.font_parameters[font]["islocal"]: 708 fl = os.path.join(vedo.fonts_path, f"{font}.ttf") 709 else: 710 try: 711 fl = vedo.file_io.download(f"https://vedo.embl.es/fonts/{font}.ttf", verbose=False) 712 except: 713 vedo.logger.warning(f"Could not download https://vedo.embl.es/fonts/{font}.ttf") 714 fl = os.path.join(vedo.fonts_path, "Normografo.ttf") 715 else: 716 if font.startswith("https://"): 717 fl = vedo.file_io.download(font, verbose=False) 718 elif os.path.isfile(font): 719 fl = font # assume user is passing a valid file 720 else: 721 if font.endswith(".ttf"): 722 vedo.logger.error( 723 f"Could not set font file {font}" 724 f"-> using default: {vedo.settings.default_font}" 725 ) 726 else: 727 vedo.settings.default_font = "Normografo" 728 vedo.logger.error( 729 f"Could not set font name {font}" 730 f" -> using default: Normografo\n" 731 f"Check out https://vedo.embl.es/fonts for additional fonts\n" 732 f"Type 'vedo -r fonts' to see available fonts" 733 ) 734 fl = get_font_path(vedo.settings.default_font) 735 return fl 736 737 738def is_sequence(arg): 739 """Check if the input is iterable.""" 740 if hasattr(arg, "strip"): 741 return False 742 if hasattr(arg, "__getslice__"): 743 return True 744 if hasattr(arg, "__iter__"): 745 return True 746 return False 747 748 749def is_ragged(arr, deep=False): 750 """ 751 A ragged array in Python is an array with arrays of different 752 lengths as its elements. To check if an array is ragged, 753 we iterate through the elements and check if their lengths are the same. 754 755 Example: 756 ```python 757 arr = [[1, 2, 3], [[4, 5], [6], 1], [7, 8, 9]] 758 print(is_ragged(arr, deep=True)) # output: True 759 ``` 760 """ 761 if is_sequence(arr[0]): 762 length = len(arr[0]) 763 for i in range(1, len(arr)): 764 if len(arr[i]) != length or (deep and is_ragged(arr[i])): 765 return True 766 return False 767 return False 768 769 770def flatten(list_to_flatten): 771 """Flatten out a list.""" 772 773 def _genflatten(lst): 774 for elem in lst: 775 if isinstance(elem, (list, tuple)): 776 for x in flatten(elem): 777 yield x 778 else: 779 yield elem 780 781 return list(_genflatten(list_to_flatten)) 782 783 784def humansort(alist): 785 """ 786 Sort in place a given list the way humans expect. 787 788 E.g. `['file11', 'file1'] -> ['file1', 'file11']` 789 790 .. warning:: input list is modified in-place by this function. 791 """ 792 import re 793 794 def alphanum_key(s): 795 # Turn a string into a list of string and number chunks. 796 # e.g. "z23a" -> ["z", 23, "a"] 797 def tryint(s): 798 if s.isdigit(): 799 return int(s) 800 return s 801 802 return [tryint(c) for c in re.split("([0-9]+)", s)] 803 804 alist.sort(key=alphanum_key) 805 return alist # NB: input list is modified 806 807 808def sort_by_column(arr, nth, invert=False): 809 """Sort a numpy array by its `n-th` column.""" 810 arr = np.asarray(arr) 811 arr = arr[arr[:, nth].argsort()] 812 if invert: 813 return np.flip(arr, axis=0) 814 return arr 815 816 817def point_in_triangle(p, p1, p2, p3): 818 """ 819 Return True if a point is inside (or above/below) 820 a triangle defined by 3 points in space. 821 """ 822 p1 = np.array(p1) 823 u = p2 - p1 824 v = p3 - p1 825 n = np.cross(u, v) 826 w = p - p1 827 ln = np.dot(n, n) 828 if not ln: 829 return None # degenerate triangle 830 gamma = (np.dot(np.cross(u, w), n)) / ln 831 if 0 < gamma < 1: 832 beta = (np.dot(np.cross(w, v), n)) / ln 833 if 0 < beta < 1: 834 alpha = 1 - gamma - beta 835 if 0 < alpha < 1: 836 return True 837 return False 838 839 840def intersection_ray_triangle(P0, P1, V0, V1, V2): 841 """ 842 Fast intersection between a directional ray defined by `P0,P1` 843 and triangle `V0, V1, V2`. 844 845 Returns the intersection point or 846 - `None` if triangle is degenerate, or ray is parallel to triangle plane. 847 - `False` if no intersection, or ray direction points away from triangle. 848 """ 849 # Credits: http://geomalgorithms.com/a06-_intersect-2.html 850 # Get triangle edge vectors and plane normal 851 # todo : this is slow should check 852 # https://vtk.org/doc/nightly/html/classvtkCell.html#aa850382213d7b8693f0eeec0209c347b 853 V0 = np.asarray(V0, dtype=float) 854 P0 = np.asarray(P0, dtype=float) 855 u = V1 - V0 856 v = V2 - V0 857 n = np.cross(u, v) 858 if not np.abs(v).sum(): # triangle is degenerate 859 return None # do not deal with this case 860 861 rd = P1 - P0 # ray direction vector 862 w0 = P0 - V0 863 a = -np.dot(n, w0) 864 b = np.dot(n, rd) 865 if not b: # ray is parallel to triangle plane 866 return None 867 868 # Get intersect point of ray with triangle plane 869 r = a / b 870 if r < 0.0: # ray goes away from triangle 871 return False # => no intersect 872 873 # Gor a segment, also test if (r > 1.0) => no intersect 874 I = P0 + r * rd # intersect point of ray and plane 875 876 # is I inside T? 877 uu = np.dot(u, u) 878 uv = np.dot(u, v) 879 vv = np.dot(v, v) 880 w = I - V0 881 wu = np.dot(w, u) 882 wv = np.dot(w, v) 883 D = uv * uv - uu * vv 884 885 # Get and test parametric coords 886 s = (uv * wv - vv * wu) / D 887 if s < 0.0 or s > 1.0: # I is outside T 888 return False 889 t = (uv * wu - uu * wv) / D 890 if t < 0.0 or (s + t) > 1.0: # I is outside T 891 return False 892 return I # I is in T 893 894 895def triangle_solver(**input_dict): 896 """ 897 Solve a triangle from any 3 known elements. 898 (Note that there might be more than one solution or none). 899 Angles are in radians. 900 901 Example: 902 ```python 903 print(triangle_solver(a=3, b=4, c=5)) 904 print(triangle_solver(a=3, ac=0.9273, ab=1.5716)) 905 print(triangle_solver(a=3, b=4, ab=1.5716)) 906 print(triangle_solver(b=4, bc=.64, ab=1.5716)) 907 print(triangle_solver(c=5, ac=.9273, bc=0.6435)) 908 print(triangle_solver(a=3, c=5, bc=0.6435)) 909 print(triangle_solver(b=4, c=5, ac=0.927)) 910 ``` 911 """ 912 a = input_dict.get("a") 913 b = input_dict.get("b") 914 c = input_dict.get("c") 915 ab = input_dict.get("ab") 916 bc = input_dict.get("bc") 917 ac = input_dict.get("ac") 918 919 if ab and bc: 920 ac = np.pi - bc - ab 921 elif bc and ac: 922 ab = np.pi - bc - ac 923 elif ab and ac: 924 bc = np.pi - ab - ac 925 926 if a is not None and b is not None and c is not None: 927 ab = np.arccos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b)) 928 sinab = np.sin(ab) 929 ac = np.arcsin(a / c * sinab) 930 bc = np.arcsin(b / c * sinab) 931 932 elif a is not None and b is not None and ab is not None: 933 c = np.sqrt(a ** 2 + b ** 2 - 2 * a * b * np.cos(ab)) 934 sinab = np.sin(ab) 935 ac = np.arcsin(a / c * sinab) 936 bc = np.arcsin(b / c * sinab) 937 938 elif a is not None and ac is not None and ab is not None: 939 h = a * np.sin(ac) 940 b = h / np.sin(bc) 941 c = b * np.cos(bc) + a * np.cos(ac) 942 943 elif b is not None and bc is not None and ab is not None: 944 h = b * np.sin(bc) 945 a = h / np.sin(ac) 946 c = np.sqrt(a * a + b * b) 947 948 elif c is not None and ac is not None and bc is not None: 949 h = c * np.sin(bc) 950 b1 = c * np.cos(bc) 951 b2 = h / np.tan(ab) 952 b = b1 + b2 953 a = np.sqrt(b2 * b2 + h * h) 954 955 elif a is not None and c is not None and bc is not None: 956 # double solution 957 h = c * np.sin(bc) 958 k = np.sqrt(a * a - h * h) 959 omega = np.arcsin(k / a) 960 cosbc = np.cos(bc) 961 b = c * cosbc - k 962 phi = np.pi / 2 - bc - omega 963 ac = phi 964 ab = np.pi - ac - bc 965 if k: 966 b2 = c * cosbc + k 967 ac2 = phi + 2 * omega 968 ab2 = np.pi - ac2 - bc 969 return [ 970 {"a": a, "b": b, "c": c, "ab": ab, "bc": bc, "ac": ac}, 971 {"a": a, "b": b2, "c": c, "ab": ab2, "bc": bc, "ac": ac2}, 972 ] 973 974 elif b is not None and c is not None and ac is not None: 975 # double solution 976 h = c * np.sin(ac) 977 k = np.sqrt(b * b - h * h) 978 omega = np.arcsin(k / b) 979 cosac = np.cos(ac) 980 a = c * cosac - k 981 phi = np.pi / 2 - ac - omega 982 bc = phi 983 ab = np.pi - bc - ac 984 if k: 985 a2 = c * cosac + k 986 bc2 = phi + 2 * omega 987 ab2 = np.pi - ac - bc2 988 return [ 989 {"a": a, "b": b, "c": c, "ab": ab, "bc": bc, "ac": ac}, 990 {"a": a2, "b": b, "c": c, "ab": ab2, "bc": bc2, "ac": ac}, 991 ] 992 993 else: 994 vedo.logger.error(f"Case {input_dict} is not supported.") 995 return [] 996 997 return [{"a": a, "b": b, "c": c, "ab": ab, "bc": bc, "ac": ac}] 998 999 1000############################################################################# 1001def point_line_distance(p, p1, p2): 1002 """ 1003 Compute the distance of a point to a line (not the segment) 1004 defined by `p1` and `p2`. 1005 """ 1006 return np.sqrt(vtk.vtkLine.DistanceToLine(p, p1, p2)) 1007 1008 1009def closest(point, points, n=1, return_ids=False, use_tree=False): 1010 """ 1011 Returns the distances and the closest point(s) to the given set of points. 1012 Needs `scipy.spatial` library. 1013 1014 Arguments: 1015 n : (int) 1016 the nr of closest points to return 1017 return_ids : (bool) 1018 return the ids instead of the points coordinates 1019 use_tree : (bool) 1020 build a `scipy.spatial.KDTree`. 1021 An already existing one can be passed to avoid rebuilding. 1022 """ 1023 from scipy.spatial import distance, KDTree 1024 1025 points = np.asarray(points) 1026 if n == 1: 1027 dists = distance.cdist([point], points) 1028 closest_idx = np.argmin(dists) 1029 else: 1030 if use_tree: 1031 if isinstance(use_tree, KDTree): # reuse 1032 tree = use_tree 1033 else: 1034 tree = KDTree(points) 1035 dists, closest_idx = tree.query([point], k=n) 1036 closest_idx = closest_idx[0] 1037 else: 1038 dists = distance.cdist([point], points) 1039 closest_idx = np.argsort(dists)[0][:n] 1040 if return_ids: 1041 return dists, closest_idx 1042 else: 1043 return dists, points[closest_idx] 1044 1045 1046############################################################################# 1047def lin_interpolate(x, rangeX, rangeY): 1048 """ 1049 Interpolate linearly the variable `x` in `rangeX` onto the new `rangeY`. 1050 If `x` is a 3D vector the linear weight is the distance to the two 3D `rangeX` vectors. 1051 1052 E.g. if `x` runs in `rangeX=[x0,x1]` and I want it to run in `rangeY=[y0,y1]` then 1053 1054 `y = lin_interpolate(x, rangeX, rangeY)` will interpolate `x` onto `rangeY`. 1055 1056 Examples: 1057 - [lin_interpolate.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/lin_interpolate.py) 1058 1059 ![](https://vedo.embl.es/images/basic/linInterpolate.png) 1060 """ 1061 if is_sequence(x): 1062 x = np.asarray(x) 1063 x0, x1 = np.asarray(rangeX) 1064 y0, y1 = np.asarray(rangeY) 1065 dx = x1 - x0 1066 dxn = np.linalg.norm(dx) 1067 if not dxn: 1068 return y0 1069 s = np.linalg.norm(x - x0) / dxn 1070 t = np.linalg.norm(x - x1) / dxn 1071 st = s + t 1072 out = y0 * (t / st) + y1 * (s / st) 1073 1074 else: # faster 1075 1076 x0 = rangeX[0] 1077 dx = rangeX[1] - x0 1078 if not dx: 1079 return rangeY[0] 1080 s = (x - x0) / dx 1081 out = rangeY[0] * (1 - s) + rangeY[1] * s 1082 return out 1083 1084 1085def get_uv(p, x, v): 1086 """ 1087 Obtain the texture uv-coords of a point p belonging to a face that has point 1088 coordinates (x0, x1, x2) with the corresponding uv-coordinates v=(v0, v1, v2). 1089 All p and x0,x1,x2 are 3D-vectors, while v are their 2D uv-coordinates. 1090 1091 Example: 1092 ```python 1093 from vedo import * 1094 1095 pic = Picture(dataurl+"coloured_cube_faces.jpg") 1096 cb = Mesh(dataurl+"coloured_cube.obj").lighting("off").texture(pic) 1097 1098 cbpts = cb.points() 1099 faces = cb.faces() 1100 uv = cb.pointdata["Material"] 1101 1102 pt = [-0.2, 0.75, 2] 1103 pr = cb.closest_point(pt) 1104 1105 idface = cb.closest_point(pt, return_cell_id=True) 1106 idpts = faces[idface] 1107 uv_face = uv[idpts] 1108 1109 uv_pr = utils.get_uv(pr, cbpts[idpts], uv_face) 1110 print("interpolated uv =", uv_pr) 1111 1112 sx, sy = pic.dimensions() 1113 i_interp_uv = uv_pr * [sy, sx] 1114 ix, iy = i_interp_uv.astype(int) 1115 mpic = pic.tomesh() 1116 rgba = mpic.pointdata["RGBA"].reshape(sy, sx, 3) 1117 print("color =", rgba[ix, iy]) 1118 1119 show( 1120 [[cb, Point(pr), cb.labels("Material")], 1121 [pic, Point(i_interp_uv)]], 1122 N=2, axes=1, sharecam=False, 1123 ).close() 1124 ``` 1125 ![](https://vedo.embl.es/images/feats/utils_get_uv.png) 1126 """ 1127 # Vector vp=p-x0 is representable as alpha*s + beta*t, 1128 # where s = x1-x0 and t = x2-x0, in matrix form 1129 # vp = [alpha, beta] . matrix(s,t) 1130 # M = matrix(s,t) is 2x3 matrix, so (alpha, beta) can be found by 1131 # inverting any of its minor A with non-zero determinant. 1132 # Once found, uv-coords of p are vt0 + alpha (vt1-v0) + beta (vt2-v0) 1133 1134 p = np.asarray(p) 1135 x0, x1, x2 = np.asarray(x)[:3] 1136 vt0, vt1, vt2 = np.asarray(v)[:3] 1137 1138 s = x1 - x0 1139 t = x2 - x0 1140 vs = vt1 - vt0 1141 vt = vt2 - vt0 1142 vp = p - x0 1143 1144 # finding a minor with independent rows 1145 M = np.matrix([s, t]) 1146 mnr = [0, 1] 1147 A = M[:, mnr] 1148 if np.abs(np.linalg.det(A)) < 0.000001: 1149 mnr = [0, 2] 1150 A = M[:, mnr] 1151 if np.abs(np.linalg.det(A)) < 0.000001: 1152 mnr = [1, 2] 1153 A = M[:, mnr] 1154 Ainv = np.linalg.inv(A) 1155 alpha_beta = vp[mnr].dot(Ainv) # [alpha, beta] 1156 return np.asarray(vt0 + alpha_beta.dot(np.matrix([vs, vt])))[0] 1157 1158 1159def vector(x, y=None, z=0.0, dtype=np.float64): 1160 """ 1161 Return a 3D numpy array representing a vector. 1162 1163 If `y` is `None`, assume input is already in the form `[x,y,z]`. 1164 """ 1165 if y is None: # assume x is already [x,y,z] 1166 return np.asarray(x, dtype=dtype) 1167 return np.array([x, y, z], dtype=dtype) 1168 1169 1170def versor(x, y=None, z=0.0, dtype=np.float64): 1171 """Return the unit vector. Input can be a list of vectors.""" 1172 v = vector(x, y, z, dtype) 1173 if isinstance(v[0], np.ndarray): 1174 return np.divide(v, mag(v)[:, None]) 1175 return v / mag(v) 1176 1177 1178def mag(v): 1179 """Get the magnitude of a vector or array of vectors.""" 1180 v = np.asarray(v) 1181 if v.ndim == 1: 1182 return np.linalg.norm(v) 1183 return np.linalg.norm(v, axis=1) 1184 1185 1186def mag2(v): 1187 """Get the squared magnitude of a vector or array of vectors.""" 1188 v = np.asarray(v) 1189 if v.ndim == 1: 1190 return np.square(v).sum() 1191 return np.square(v).sum(axis=1) 1192 1193 1194def is_integer(n): 1195 """Check if input is an integer.""" 1196 try: 1197 float(n) 1198 except (ValueError, TypeError): 1199 return False 1200 else: 1201 return float(n).is_integer() 1202 1203 1204def is_number(n): 1205 """Check if input is a number""" 1206 try: 1207 float(n) 1208 return True 1209 except (ValueError, TypeError): 1210 return False 1211 1212 1213def round_to_digit(x, p): 1214 """Round a real number to the specified number of significant digits.""" 1215 if not x: 1216 return 0 1217 r = np.round(x, p - int(np.floor(np.log10(abs(x)))) - 1) 1218 if int(r) == r: 1219 return int(r) 1220 return r 1221 1222 1223def pack_spheres(bounds, radius): 1224 """ 1225 Packing spheres into a bounding box. 1226 Returns a numpy array of sphere centers. 1227 """ 1228 h = 0.8164965 / 2 1229 d = 0.8660254 1230 a = 0.288675135 1231 1232 if is_sequence(bounds): 1233 x0, x1, y0, y1, z0, z1 = bounds 1234 else: 1235 x0, x1, y0, y1, z0, z1 = bounds.bounds() 1236 1237 x = np.arange(x0, x1, radius) 1238 nul = np.zeros_like(x) 1239 nz = int((z1 - z0) / radius / h / 2 + 1.5) 1240 ny = int((y1 - y0) / radius / d + 1.5) 1241 1242 pts = [] 1243 for iz in range(nz): 1244 z = z0 + nul + iz * h * radius 1245 dx, dy, dz = [radius * 0.5, radius * a, iz * h * radius] 1246 for iy in range(ny): 1247 y = y0 + nul + iy * d * radius 1248 if iy % 2: 1249 xs = x 1250 else: 1251 xs = x + radius * 0.5 1252 if iz % 2: 1253 p = np.c_[xs, y, z] + [dx, dy, dz] 1254 else: 1255 p = np.c_[xs, y, z] + [0, 0, dz] 1256 pts += p.tolist() 1257 return np.array(pts) 1258 1259 1260def precision(x, p, vrange=None, delimiter="e"): 1261 """ 1262 Returns a string representation of `x` formatted to precision `p`. 1263 1264 Set `vrange` to the range in which x exists (to snap x to '0' if below precision). 1265 """ 1266 # Based on the webkit javascript implementation 1267 # `from here <https://code.google.com/p/webkit-mirror/source/browse/JavaScriptCore/kjs/number_object.cpp>`_, 1268 # and implemented by `randlet <https://github.com/randlet/to-precision>`_. 1269 # Modified for vedo by M.Musy 2020 1270 1271 if isinstance(x, str): # do nothing 1272 return x 1273 1274 if is_sequence(x): 1275 out = "(" 1276 nn = len(x) - 1 1277 for i, ix in enumerate(x): 1278 1279 try: 1280 if np.isnan(ix): 1281 return "NaN" 1282 except: 1283 # cannot handle list of list 1284 continue 1285 1286 out += precision(ix, p) 1287 if i < nn: 1288 out += ", " 1289 return out + ")" ############ <-- 1290 1291 if np.isnan(x): 1292 return "NaN" 1293 1294 x = float(x) 1295 1296 if x == 0.0 or (vrange is not None and abs(x) < vrange / pow(10, p)): 1297 return "0" 1298 1299 out = [] 1300 if x < 0: 1301 out.append("-") 1302 x = -x 1303 1304 e = int(math.log10(x)) 1305 tens = math.pow(10, e - p + 1) 1306 n = math.floor(x / tens) 1307 1308 if n < math.pow(10, p - 1): 1309 e = e - 1 1310 tens = math.pow(10, e - p + 1) 1311 n = math.floor(x / tens) 1312 1313 if abs((n + 1.0) * tens - x) <= abs(n * tens - x): 1314 n = n + 1 1315 1316 if n >= math.pow(10, p): 1317 n = n / 10.0 1318 e = e + 1 1319 1320 m = "%.*g" % (p, n) 1321 if e < -2 or e >= p: 1322 out.append(m[0]) 1323 if p > 1: 1324 out.append(".") 1325 out.extend(m[1:p]) 1326 out.append(delimiter) 1327 if e > 0: 1328 out.append("+") 1329 out.append(str(e)) 1330 elif e == (p - 1): 1331 out.append(m) 1332 elif e >= 0: 1333 out.append(m[: e + 1]) 1334 if e + 1 < len(m): 1335 out.append(".") 1336 out.extend(m[e + 1 :]) 1337 else: 1338 out.append("0.") 1339 out.extend(["0"] * -(e + 1)) 1340 out.append(m) 1341 return "".join(out) 1342 1343 1344################################################################################## 1345# 2d ###### 1346def cart2pol(x, y): 1347 """2D Cartesian to Polar coordinates conversion.""" 1348 theta = np.arctan2(y, x) 1349 rho = np.hypot(x, y) 1350 return np.array([rho, theta]) 1351 1352 1353def pol2cart(rho, theta): 1354 """2D Polar to Cartesian coordinates conversion.""" 1355 x = rho * np.cos(theta) 1356 y = rho * np.sin(theta) 1357 return np.array([x, y]) 1358 1359 1360# 3d ###### 1361def cart2spher(x, y, z): 1362 """3D Cartesian to Spherical coordinate conversion.""" 1363 hxy = np.hypot(x, y) 1364 rho = np.hypot(hxy, z) 1365 theta = np.arctan2(hxy, z) 1366 phi = np.arctan2(y, x) 1367 return np.array([rho, theta, phi]) 1368 1369 1370def spher2cart(rho, theta, phi): 1371 """3D Spherical to Cartesian coordinate conversion.""" 1372 st = np.sin(theta) 1373 sp = np.sin(phi) 1374 ct = np.cos(theta) 1375 cp = np.cos(phi) 1376 rst = rho * st 1377 x = rst * cp 1378 y = rst * sp 1379 z = rho * ct 1380 return np.array([x, y, z]) 1381 1382 1383def cart2cyl(x, y, z): 1384 """3D Cartesian to Cylindrical coordinate conversion.""" 1385 rho = np.sqrt(x * x + y * y) 1386 theta = np.arctan2(y, x) 1387 return np.array([rho, theta, z]) 1388 1389 1390def cyl2cart(rho, theta, z): 1391 """3D Cylindrical to Cartesian coordinate conversion.""" 1392 x = rho * np.cos(theta) 1393 y = rho * np.sin(theta) 1394 return np.array([x, y, z]) 1395 1396 1397def cyl2spher(rho, theta, z): 1398 """3D Cylindrical to Spherical coordinate conversion.""" 1399 rhos = np.sqrt(rho * rho + z * z) 1400 phi = np.arctan2(rho, z) 1401 return np.array([rhos, phi, theta]) 1402 1403 1404def spher2cyl(rho, theta, phi): 1405 """3D Spherical to Cylindrical coordinate conversion.""" 1406 rhoc = rho * np.sin(theta) 1407 z = rho * np.cos(theta) 1408 return np.array([rhoc, phi, z]) 1409 1410 1411################################################################################## 1412def grep(filename, tag, column=None, first_occurrence_only=False): 1413 """Greps the line in a file that starts with a specific `tag` string inside the file.""" 1414 import re 1415 1416 with open(filename, "r", encoding="UTF-8") as afile: 1417 content = [] 1418 for line in afile: 1419 if re.search(tag, line): 1420 c = line.split() 1421 c[-1] = c[-1].replace("\n", "") 1422 if column is not None: 1423 c = c[column] 1424 content.append(c) 1425 if first_occurrence_only: 1426 break 1427 return content 1428 1429 1430def print_info(obj): 1431 """Print information about a `vedo` object.""" 1432 1433 def _print_data(poly, c): 1434 ptdata = poly.GetPointData() 1435 cldata = poly.GetCellData() 1436 fldata = poly.GetFieldData() 1437 if ptdata.GetNumberOfArrays() + cldata.GetNumberOfArrays(): 1438 1439 for i in range(ptdata.GetNumberOfArrays()): 1440 name = ptdata.GetArrayName(i) 1441 if name and ptdata.GetArray(i): 1442 vedo.printc("pointdata".ljust(14) + ": ", c=c, bold=True, end="") 1443 try: 1444 tt, _ = array_types[ptdata.GetArray(i).GetDataType()] 1445 except: 1446 tt = "VTKTYPE" + str(ptdata.GetArray(i).GetDataType()) 1447 ncomp = ptdata.GetArray(i).GetNumberOfComponents() 1448 rng = ptdata.GetArray(i).GetRange() 1449 vedo.printc(f'"{name}" ({ncomp} {tt}),', c=c, bold=False, end="") 1450 vedo.printc( 1451 " range=(" + precision(rng[0], 3) + "," + precision(rng[1], 3) + ")", 1452 c=c, 1453 bold=False, 1454 ) 1455 1456 if ptdata.GetScalars(): 1457 vedo.printc("active scalars".ljust(14) + ": ", c=c, bold=True, end="") 1458 vedo.printc(ptdata.GetScalars().GetName(), "(pointdata) ", c=c, bold=False) 1459 1460 if ptdata.GetVectors(): 1461 vedo.printc("active vectors".ljust(14) + ": ", c=c, bold=True, end="") 1462 vedo.printc(ptdata.GetVectors().GetName(), "(pointdata) ", c=c, bold=False) 1463 1464 if ptdata.GetTensors(): 1465 vedo.printc("active tensors".ljust(14) + ": ", c=c, bold=True, end="") 1466 vedo.printc(ptdata.GetTensors().GetName(), "(pointdata) ", c=c, bold=False) 1467 1468 # same for cells 1469 for i in range(cldata.GetNumberOfArrays()): 1470 name = cldata.GetArrayName(i) 1471 if name and cldata.GetArray(i): 1472 vedo.printc("celldata".ljust(14) + ": ", c=c, bold=True, end="") 1473 try: 1474 tt, _ = array_types[cldata.GetArray(i).GetDataType()] 1475 except: 1476 tt = cldata.GetArray(i).GetDataType() 1477 ncomp = cldata.GetArray(i).GetNumberOfComponents() 1478 rng = cldata.GetArray(i).GetRange() 1479 vedo.printc(f'"{name}" ({ncomp} {tt}),', c=c, bold=False, end="") 1480 vedo.printc( 1481 " range=(" + precision(rng[0], 4) + "," + precision(rng[1], 4) + ")", 1482 c=c, 1483 bold=False, 1484 ) 1485 1486 if cldata.GetScalars(): 1487 vedo.printc("active scalars".ljust(14) + ": ", c=c, bold=True, end="") 1488 vedo.printc(cldata.GetScalars().GetName(), "(celldata)", c=c, bold=False) 1489 1490 if cldata.GetVectors(): 1491 vedo.printc("active vectors".ljust(14) + ": ", c=c, bold=True, end="") 1492 vedo.printc(cldata.GetVectors().GetName(), "(celldata)", c=c, bold=False) 1493 1494 for i in range(fldata.GetNumberOfArrays()): 1495 name = fldata.GetArrayName(i) 1496 if name and fldata.GetAbstractArray(i): 1497 arr = fldata.GetAbstractArray(i) 1498 vedo.printc("metadata".ljust(14) + ": ", c=c, bold=True, end="") 1499 ncomp = arr.GetNumberOfComponents() 1500 nvals = arr.GetNumberOfValues() 1501 vedo.printc(f'"{name}" ({ncomp} components, {nvals} values)', c=c, bold=False) 1502 1503 else: 1504 vedo.printc("mesh data".ljust(14) + ":", c=c, bold=True, end=" ") 1505 vedo.printc("no point or cell data is present.", c=c, bold=False) 1506 1507 ################################ 1508 def _printvtkactor(actor): 1509 1510 if not actor.GetPickable(): 1511 return 1512 1513 mapper = actor.GetMapper() 1514 if hasattr(actor, "polydata"): 1515 poly = actor.polydata() 1516 else: 1517 poly = mapper.GetInput() 1518 1519 pro = actor.GetProperty() 1520 pos = actor.GetPosition() 1521 bnds = actor.bounds() 1522 col = precision(pro.GetColor(), 3) 1523 alpha = pro.GetOpacity() 1524 npt = poly.GetNumberOfPoints() 1525 npl = poly.GetNumberOfPolys() 1526 nln = poly.GetNumberOfLines() 1527 1528 vedo.printc("Mesh/Points".ljust(70), c="g", bold=True, invert=True, dim=1, end="") 1529 1530 if hasattr(actor, "info") and "legend" in actor.info.keys() and actor.info["legend"]: 1531 vedo.printc("legend".ljust(14) + ": ", c="g", bold=True, end="") 1532 vedo.printc(actor.info["legend"], c="g", bold=False) 1533 else: 1534 print() 1535 1536 if hasattr(actor, "name") and actor.name: 1537 vedo.printc("name".ljust(14) + ": ", c="g", bold=True, end="") 1538 vedo.printc(actor.name, c="g", bold=False) 1539 1540 if hasattr(actor, "filename") and actor.filename: 1541 vedo.printc("file name".ljust(14) + ": ", c="g", bold=True, end="") 1542 vedo.printc(actor.filename, c="g", bold=False) 1543 1544 if not actor.GetMapper().GetScalarVisibility(): 1545 vedo.printc("color".ljust(14) + ": ", c="g", bold=True, end="") 1546 cname = vedo.colors.get_color_name(pro.GetColor()) 1547 vedo.printc(f"{cname}, rgb={col}, alpha={alpha}", c="g", bold=False) 1548 1549 if actor.GetBackfaceProperty(): 1550 bcol = actor.GetBackfaceProperty().GetDiffuseColor() 1551 cname = vedo.colors.get_color_name(bcol) 1552 vedo.printc("back color".ljust(14) + ": ", c="g", bold=True, end="") 1553 vedo.printc(f"{cname}, rgb={precision(bcol,3)}", c="g", bold=False) 1554 1555 vedo.printc("points".ljust(14) + ":", npt, c="g", bold=True) 1556 # ncl = poly.GetNumberOfCells() 1557 # vedo.printc("cells".ljust(14)+":", ncl, c="g", bold=True) 1558 vedo.printc("polygons".ljust(14) + ":", npl, c="g", bold=True) 1559 if nln: 1560 vedo.printc("lines".ljust(14) + ":", nln, c="g", bold=True) 1561 vedo.printc("position".ljust(14) + ":", pos, c="g", bold=True) 1562 1563 if hasattr(actor, "GetScale"): 1564 vedo.printc("scale".ljust(14) + ":", c="g", bold=True, end=" ") 1565 vedo.printc(precision(actor.GetScale(), 3), c="g", bold=False) 1566 1567 if hasattr(actor, "polydata") and actor.npoints: 1568 vedo.printc("center of mass".ljust(14) + ":", c="g", bold=True, end=" ") 1569 cm = tuple(actor.center_of_mass()) 1570 vedo.printc(precision(cm, 3), c="g", bold=False) 1571 1572 vedo.printc("average size".ljust(14) + ":", c="g", bold=True, end=" ") 1573 vedo.printc(precision(actor.average_size(), 6), c="g", bold=False) 1574 1575 vedo.printc("diagonal size".ljust(14) + ":", c="g", bold=True, end=" ") 1576 vedo.printc(precision(actor.diagonal_size(), 6), c="g", bold=False) 1577 1578 vedo.printc("bounds".ljust(14) + ":", c="g", bold=True, end=" ") 1579 bx1, bx2 = precision(bnds[0], 3), precision(bnds[1], 3) 1580 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="g", bold=False, end="") 1581 by1, by2 = precision(bnds[2], 3), precision(bnds[3], 3) 1582 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="g", bold=False, end="") 1583 bz1, bz2 = precision(bnds[4], 3), precision(bnds[5], 3) 1584 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="g", bold=False) 1585 1586 _print_data(poly, "g") 1587 1588 if hasattr(actor, "picked3d") and actor.picked3d is not None: 1589 idpt = actor.closest_point(actor.picked3d, return_point_id=True) 1590 idcell = actor.closest_point(actor.picked3d, return_cell_id=True) 1591 vedo.printc( 1592 "clicked point".ljust(14) + ":", 1593 precision(actor.picked3d, 6), 1594 f"pointID={idpt}, cellID={idcell}", 1595 c="g", 1596 bold=True, 1597 ) 1598 1599 if obj is None: 1600 return 1601 1602 if isinstance(obj, np.ndarray): 1603 cf = "y" 1604 vedo.printc("Numpy Array".ljust(70), c=cf, invert=True) 1605 vedo.printc(obj, c=cf) 1606 vedo.printc("shape".ljust(8) + ":", obj.shape, c=cf) 1607 vedo.printc("range".ljust(8) + f": ({np.min(obj)}, {np.max(obj)})", c=cf) 1608 vedo.printc("mean".ljust(8) + ":", np.mean(obj), c=cf) 1609 vedo.printc("std_dev".ljust(8) + ":", np.std(obj), c=cf) 1610 if len(obj.shape) >= 2: 1611 vedo.printc("Axis 0".ljust(8) + ":", c=cf, italic=1) 1612 vedo.printc("\tmin :", np.min(obj, axis=0), c=cf) 1613 vedo.printc("\tmax :", np.max(obj, axis=0), c=cf) 1614 vedo.printc("\tmean:", np.mean(obj, axis=0), c=cf) 1615 if obj.shape[1] > 3: 1616 vedo.printc("Axis 1".ljust(8) + ":", c=cf, italic=1) 1617 tmin = str(np.min(obj, axis=1).tolist()[:2]).replace("]", ", ...") 1618 tmax = str(np.max(obj, axis=1).tolist()[:2]).replace("]", ", ...") 1619 tmea = str(np.mean(obj, axis=1).tolist()[:2]).replace("]", ", ...") 1620 vedo.printc(f"\tmin : {tmin}", c=cf) 1621 vedo.printc(f"\tmax : {tmax}", c=cf) 1622 vedo.printc(f"\tmean: {tmea}", c=cf) 1623 1624 elif isinstance(obj, vedo.Points): 1625 _printvtkactor(obj) 1626 1627 elif isinstance(obj, vedo.Assembly): 1628 vedo.printc("Assembly".ljust(75), c="g", bold=True, invert=True) 1629 1630 pos = obj.GetPosition() 1631 bnds = obj.GetBounds() 1632 vedo.printc("position".ljust(14) + ": ", c="g", bold=True, end="") 1633 vedo.printc(pos, c="g", bold=False) 1634 1635 vedo.printc("bounds".ljust(14) + ": ", c="g", bold=True, end="") 1636 bx1, bx2 = precision(bnds[0], 3), precision(bnds[1], 3) 1637 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="g", bold=False, end="") 1638 by1, by2 = precision(bnds[2], 3), precision(bnds[3], 3) 1639 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="g", bold=False, end="") 1640 bz1, bz2 = precision(bnds[4], 3), precision(bnds[5], 3) 1641 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="g", bold=False) 1642 1643 cl = vtk.vtkPropCollection() 1644 obj.GetActors(cl) 1645 cl.InitTraversal() 1646 for _ in range(obj.GetNumberOfPaths()): 1647 act = vtk.vtkActor.SafeDownCast(cl.GetNextProp()) 1648 if isinstance(act, vtk.vtkActor): 1649 _printvtkactor(act) 1650 1651 elif isinstance(obj, vedo.TetMesh): 1652 cf = "m" 1653 vedo.printc("TetMesh".ljust(70), c=cf, bold=True, invert=True) 1654 pos = obj.GetPosition() 1655 bnds = obj.GetBounds() 1656 ug = obj.inputdata() 1657 vedo.printc("nr. of tetras".ljust(14) + ": ", c=cf, bold=True, end="") 1658 vedo.printc(ug.GetNumberOfCells(), c=cf, bold=False) 1659 vedo.printc("position".ljust(14) + ": ", c=cf, bold=True, end="") 1660 vedo.printc(pos, c=cf, bold=False) 1661 vedo.printc("bounds".ljust(14) + ": ", c=cf, bold=True, end="") 1662 bx1, bx2 = precision(bnds[0], 3), precision(bnds[1], 3) 1663 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c=cf, bold=False, end="") 1664 by1, by2 = precision(bnds[2], 3), precision(bnds[3], 3) 1665 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c=cf, bold=False, end="") 1666 bz1, bz2 = precision(bnds[4], 3), precision(bnds[5], 3) 1667 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c=cf, bold=False) 1668 _print_data(ug, cf) 1669 1670 elif isinstance(obj, (vedo.volume.Volume, vedo.volume.VolumeSlice)): 1671 vedo.printc("Volume".ljust(70), c="b", bold=True, invert=True) 1672 1673 img = obj.GetMapper().GetInput() 1674 vedo.printc("origin".ljust(14) + ": ", c="b", bold=True, end="") 1675 vedo.printc(precision(obj.origin(), 6), c="b", bold=False) 1676 1677 vedo.printc("center".ljust(14) + ": ", c="b", bold=True, end="") 1678 vedo.printc(precision(obj.center(), 6), c="b", bold=False) 1679 1680 vedo.printc("dimensions".ljust(14) + ": ", c="b", bold=True, end="") 1681 vedo.printc(img.GetDimensions(), c="b", bold=False) 1682 vedo.printc("spacing".ljust(14) + ": ", c="b", bold=True, end="") 1683 vedo.printc(precision(img.GetSpacing(), 6), c="b", bold=False) 1684 # vedo.printc("data dimension".ljust(14) + ": ", c="b", bold=True, end="") 1685 # vedo.printc(img.GetDataDimension(), c="b", bold=False) 1686 1687 vedo.printc("memory size".ljust(14) + ": ", c="b", bold=True, end="") 1688 vedo.printc(int(img.GetActualMemorySize() / 1024), "MB", c="b", bold=False) 1689 1690 vedo.printc("scalar #bytes".ljust(14) + ": ", c="b", bold=True, end="") 1691 vedo.printc(img.GetScalarSize(), c="b", bold=False) 1692 1693 bnds = obj.GetBounds() 1694 vedo.printc("bounds".ljust(14) + ": ", c="b", bold=True, end="") 1695 bx1, bx2 = precision(bnds[0], 4), precision(bnds[1], 4) 1696 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="b", bold=False, end="") 1697 by1, by2 = precision(bnds[2], 4), precision(bnds[3], 4) 1698 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="b", bold=False, end="") 1699 bz1, bz2 = precision(bnds[4], 4), precision(bnds[5], 4) 1700 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="b", bold=False) 1701 1702 vedo.printc("scalar range".ljust(14) + ": ", c="b", bold=True, end="") 1703 vedo.printc(img.GetScalarRange(), c="b", bold=False) 1704 1705 print_histogram(obj, horizontal=True, logscale=True, bins=8, height=15, c="b", bold=True) 1706 1707 elif isinstance(obj, vedo.Plotter) and obj.interactor: # dumps Plotter info 1708 axtype = { 1709 0: "(no axes)", 1710 1: "(three customizable gray grid walls)", 1711 2: "(cartesian axes from origin", 1712 3: "(positive range of cartesian axes from origin", 1713 4: "(axes triad at bottom left)", 1714 5: "(oriented cube at bottom left)", 1715 6: "(mark the corners of the bounding box)", 1716 7: "(3D ruler at each side of the cartesian axes)", 1717 8: "(the vtkCubeAxesActor object)", 1718 9: "(the bounding box outline)", 1719 10: "(circles of maximum bounding box range)", 1720 11: "(show a large grid on the x-y plane)", 1721 12: "(show polar axes)", 1722 13: "(simple ruler at the bottom of the window)", 1723 14: "(the vtkCameraOrientationWidget object)", 1724 } 1725 bns, totpt = [], 0 1726 for a in obj.actors: 1727 b = a.GetBounds() 1728 if a.GetBounds() is not None: 1729 if isinstance(a, vtk.vtkActor) and a.GetMapper(): 1730 totpt += a.GetMapper().GetInput().GetNumberOfPoints() 1731 bns.append(b) 1732 if len(bns) == 0: 1733 return 1734 vedo.printc("Plotter".ljust(70), invert=True, dim=1, c="c") 1735 otit = obj.title 1736 if not otit: 1737 otit = None 1738 vedo.printc("window title".ljust(14) + ":", otit, bold=False, c="c") 1739 vedo.printc( 1740 "window size".ljust(14) + ":", 1741 obj.window.GetSize(), 1742 "- full screen size:", 1743 obj.window.GetScreenSize(), 1744 bold=False, 1745 c="c", 1746 ) 1747 vedo.printc( 1748 "actv renderer".ljust(14) + ":", 1749 "nr.", 1750 obj.renderers.index(obj.renderer), 1751 f"(of {len(obj.renderers)} renderers)", 1752 bold=False, 1753 c="c", 1754 ) 1755 vedo.printc("nr. of actors".ljust(14) + ":", len(obj.actors), bold=False, c="c", end="") 1756 vedo.printc(" (" + str(totpt), "vertices)", bold=False, c="c") 1757 max_bns = np.max(bns, axis=0) 1758 min_bns = np.min(bns, axis=0) 1759 vedo.printc("max bounds".ljust(14) + ": ", c="c", bold=False, end="") 1760 bx1, bx2 = precision(min_bns[0], 3), precision(max_bns[1], 3) 1761 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="c", bold=False, end="") 1762 by1, by2 = precision(min_bns[2], 3), precision(max_bns[3], 3) 1763 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="c", bold=False, end="") 1764 bz1, bz2 = precision(min_bns[4], 3), precision(max_bns[5], 3) 1765 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="c", bold=False) 1766 if isinstance(obj.axes, dict): 1767 obj.axes = 1 1768 if obj.axes: 1769 vedo.printc("axes style".ljust(14) + ":", obj.axes, axtype[obj.axes], bold=False, c="c") 1770 1771 elif isinstance(obj, vedo.Picture): # dumps Picture info 1772 vedo.printc("Picture".ljust(70), c="y", bold=True, invert=True) 1773 try: 1774 # generate a print thumbnail 1775 width, height = obj.dimensions() 1776 w = 45 1777 h = int(height / width * (w - 1) * 0.5 + 0.5) 1778 img_arr = obj.clone().resize([w, h]).tonumpy() 1779 h, w = img_arr.shape[:2] 1780 for x in range(h): 1781 for y in range(w): 1782 pix = img_arr[x][y] 1783 r, g, b = pix[:3] 1784 print(f"\x1b[48;2;{r};{g};{b}m", end=" ") 1785 print("\x1b[0m") 1786 except: 1787 pass 1788 1789 img = obj.GetMapper().GetInput() 1790 pos = obj.GetPosition() 1791 vedo.printc("position".ljust(14) + ": ", c="y", bold=True, end="") 1792 vedo.printc(pos, c="y", bold=False) 1793 1794 vedo.printc("dimensions".ljust(14) + ": ", c="y", bold=True, end="") 1795 vedo.printc(obj.shape, c="y", bold=False) 1796 1797 vedo.printc("memory size".ljust(14) + ": ", c="y", bold=True, end="") 1798 vedo.printc(int(img.GetActualMemorySize()), "kB", c="y", bold=False) 1799 1800 bnds = obj.GetBounds() 1801 vedo.printc("bounds".ljust(14) + ": ", c="y", bold=True, end="") 1802 bx1, bx2 = precision(bnds[0], 3), precision(bnds[1], 3) 1803 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="y", bold=False, end="") 1804 by1, by2 = precision(bnds[2], 3), precision(bnds[3], 3) 1805 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="y", bold=False, end="") 1806 bz1, bz2 = precision(bnds[4], 3), precision(bnds[5], 3) 1807 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="y", bold=False) 1808 1809 vedo.printc("intensty range".ljust(14) + ": ", c="y", bold=True, end="") 1810 vedo.printc(img.GetScalarRange(), c="y", bold=False) 1811 vedo.printc("level / window".ljust(14) + ": ", c="y", bold=True, end="") 1812 vedo.printc(obj.level(), "/", obj.window(), c="y", bold=False) 1813 1814 else: 1815 vedo.printc(str(type(obj)).ljust(70), invert=True) 1816 vedo.printc(obj) 1817 1818 1819def print_histogram( 1820 data, 1821 bins=10, 1822 height=10, 1823 logscale=False, 1824 minbin=0, 1825 horizontal=False, 1826 char="\u2588", 1827 c=None, 1828 bold=True, 1829 title="histogram", 1830 spacer="", 1831): 1832 """ 1833 Ascii histogram printing. 1834 1835 Input can be a `vedo.Volume` or `vedo.Mesh`. 1836 Returns the raw data before binning (useful when passing vtk objects). 1837 1838 Arguments: 1839 bins : (int) 1840 number of histogram bins 1841 height : (int) 1842 height of the histogram in character units 1843 logscale : (bool) 1844 use logscale for frequencies 1845 minbin : (int) 1846 ignore bins before minbin 1847 horizontal : (bool) 1848 show histogram horizontally 1849 char : (str) 1850 character to be used 1851 bold : (bool) 1852 use boldface 1853 title : (str) 1854 histogram title 1855 spacer : (str) 1856 horizontal spacer 1857 1858 Example: 1859 ```python 1860 from vedo import print_histogram 1861 import numpy as np 1862 d = np.random.normal(size=1000) 1863 data = print_histogram(d, c='blue', logscale=True, title='my scalars') 1864 data = print_histogram(d, c=1, horizontal=1) 1865 print(np.mean(data)) # data here is same as d 1866 ``` 1867 ![](https://vedo.embl.es/images/feats/print_histogram.png) 1868 """ 1869 # credits: http://pyinsci.blogspot.com/2009/10/ascii-histograms.html 1870 # adapted for vedo by M.Musy, 2019 1871 1872 if not horizontal: # better aspect ratio 1873 bins *= 2 1874 1875 isimg = isinstance(data, vtk.vtkImageData) 1876 isvol = isinstance(data, vtk.vtkVolume) 1877 if isimg or isvol: 1878 if isvol: 1879 img = data.imagedata() 1880 else: 1881 img = data 1882 dims = img.GetDimensions() 1883 nvx = min(100000, dims[0] * dims[1] * dims[2]) 1884 idxs = np.random.randint(0, min(dims), size=(nvx, 3)) 1885 data = [] 1886 for ix, iy, iz in idxs: 1887 d = img.GetScalarComponentAsFloat(ix, iy, iz, 0) 1888 data.append(d) 1889 elif isinstance(data, vtk.vtkActor): 1890 arr = data.polydata().GetPointData().GetScalars() 1891 if not arr: 1892 arr = data.polydata().GetCellData().GetScalars() 1893 if not arr: 1894 return None 1895 1896 data = vtk2numpy(arr) 1897 1898 try: 1899 h = np.histogram(data, bins=bins) 1900 except TypeError as e: 1901 vedo.logger.error(f"cannot compute histogram: {e}") 1902 return "" 1903 1904 if minbin: 1905 hi = h[0][minbin:-1] 1906 else: 1907 hi = h[0] 1908 1909 if char == "\U00002589" and horizontal: 1910 char = "\U00002586" 1911 1912 title = title.ljust(14) + ":" 1913 entrs = " entries=" + str(len(data)) 1914 if logscale: 1915 h0 = np.log10(hi + 1) 1916 maxh0 = int(max(h0) * 100) / 100 1917 title = title + entrs + " (logscale)" 1918 else: 1919 h0 = hi 1920 maxh0 = max(h0) 1921 title = title + entrs 1922 1923 def _v(): 1924 his = "" 1925 if title: 1926 his += title + "\n" 1927 bars = h0 / maxh0 * height 1928 for l in reversed(range(1, height + 1)): 1929 line = "" 1930 if l == height: 1931 line = "%s " % maxh0 1932 else: 1933 line = " |" + " " * (len(str(maxh0)) - 3) 1934 for c in bars: 1935 if c >= np.ceil(l): 1936 line += char 1937 else: 1938 line += " " 1939 line += "\n" 1940 his += line 1941 his += "%.2f" % h[1][0] + "." * (bins) + "%.2f" % h[1][-1] + "\n" 1942 return his 1943 1944 def _h(): 1945 his = "" 1946 if title: 1947 his += title + "\n" 1948 xl = ["%.2f" % n for n in h[1]] 1949 lxl = [len(l) for l in xl] 1950 bars = h0 / maxh0 * height 1951 his += spacer + " " * int(max(bars) + 2 + max(lxl)) + "%s\n" % maxh0 1952 for i, c in enumerate(bars): 1953 line = xl[i] + " " * int(max(lxl) - lxl[i]) + "| " + char * int(c) + "\n" 1954 his += spacer + line 1955 return his 1956 1957 if horizontal: 1958 height *= 2 1959 vedo.printc(_h(), c=c, bold=bold) 1960 else: 1961 vedo.printc(_v(), c=c, bold=bold) 1962 return data 1963 1964 1965def print_table(*columns, headers=None, c="g"): 1966 """ 1967 Print lists as tables. 1968 1969 Example: 1970 ```python 1971 from vedo.utils import print_table 1972 list1 = ["A", "B", "C"] 1973 list2 = [142, 220, 330] 1974 list3 = [True, False, True] 1975 headers = ["First Column", "Second Column", "Third Column"] 1976 print_table(list1, list2, list3, headers=headers) 1977 ``` 1978 1979 ![](https://vedo.embl.es/images/feats/) 1980 """ 1981 # If headers is not provided, use default header names 1982 corner = "─" 1983 if headers is None: 1984 headers = [f"Column {i}" for i in range(1, len(columns) + 1)] 1985 assert len(headers) == len(columns) 1986 1987 # Find the maximum length of the elements in each column and header 1988 max_lens = [max(len(str(x)) for x in column) for column in columns] 1989 max_len_headers = [max(len(str(header)), max_len) for header, max_len in zip(headers, max_lens)] 1990 1991 # Construct the table header 1992 header = ( 1993 "│ " 1994 + " │ ".join(header.ljust(max_len) for header, max_len in zip(headers, max_len_headers)) 1995 + " │" 1996 ) 1997 1998 # Construct the line separator 1999 line1 = "┌" + corner.join("─" * (max_len + 2) for max_len in max_len_headers) + "┐" 2000 line2 = "└" + corner.join("─" * (max_len + 2) for max_len in max_len_headers) + "┘" 2001 2002 # Print the table header 2003 vedo.printc(line1, c=c) 2004 vedo.printc(header, c=c) 2005 vedo.printc(line2, c=c) 2006 2007 # Print the data rows 2008 for row in zip(*columns): 2009 row = ( 2010 "│ " 2011 + " │ ".join(str(col).ljust(max_len) for col, max_len in zip(row, max_len_headers)) 2012 + " │" 2013 ) 2014 vedo.printc(row, bold=False, c=c) 2015 2016 # Print the line separator again to close the table 2017 vedo.printc(line2, c=c) 2018 2019 2020def make_bands(inputlist, n): 2021 """ 2022 Group values of a list into bands of equal value, where 2023 `n` is the number of bands, a positive integer > 2. 2024 2025 Returns a binned list of the same length as the input. 2026 """ 2027 if n < 2: 2028 return inputlist 2029 vmin = np.min(inputlist) 2030 vmax = np.max(inputlist) 2031 bb = np.linspace(vmin, vmax, n, endpoint=0) 2032 dr = bb[1] - bb[0] 2033 bb += dr / 2 2034 tol = dr / 2 * 1.001 2035 newlist = [] 2036 for s in inputlist: 2037 for b in bb: 2038 if abs(s - b) < tol: 2039 newlist.append(b) 2040 break 2041 return np.array(newlist) 2042 2043 2044################################################################# 2045# Functions adapted from: 2046# https://github.com/sdorkenw/MeshParty/blob/master/meshparty/trimesh_vtk.py 2047def camera_from_quaternion(pos, quaternion, distance=10000, ngl_correct=True): 2048 """ 2049 Define a `vtkCamera` with a particular orientation. 2050 2051 Arguments: 2052 pos: (np.array, list, tuple) 2053 an iterator of length 3 containing the focus point of the camera 2054 quaternion: (np.array, list, tuple) 2055 a len(4) quaternion `(x,y,z,w)` describing the rotation of the camera 2056 such as returned by neuroglancer `x,y,z,w` all in `[0,1]` range 2057 distance: (float) 2058 the desired distance from pos to the camera (default = 10000 nm) 2059 2060 Returns: 2061 `vtk.vtkCamera`, a vtk camera setup according to these rules. 2062 """ 2063 camera = vtk.vtkCamera() 2064 # define the quaternion in vtk, note the swapped order 2065 # w,x,y,z instead of x,y,z,w 2066 quat_vtk = vtk.vtkQuaterniond(quaternion[3], quaternion[0], quaternion[1], quaternion[2]) 2067 # use this to define a rotation matrix in x,y,z 2068 # right handed units 2069 M = np.zeros((3, 3), dtype=np.float32) 2070 quat_vtk.ToMatrix3x3(M) 2071 # the default camera orientation is y up 2072 up = [0, 1, 0] 2073 # calculate default camera position is backed off in positive z 2074 pos = [0, 0, distance] 2075 2076 # set the camera rototation by applying the rotation matrix 2077 camera.SetViewUp(*np.dot(M, up)) 2078 # set the camera position by applying the rotation matrix 2079 camera.SetPosition(*np.dot(M, pos)) 2080 if ngl_correct: 2081 # neuroglancer has positive y going down 2082 # so apply these azimuth and roll corrections 2083 # to fix orientatins 2084 camera.Azimuth(-180) 2085 camera.Roll(180) 2086 2087 # shift the camera posiiton and focal position 2088 # to be centered on the desired location 2089 p = camera.GetPosition() 2090 p_new = np.array(p) + pos 2091 camera.SetPosition(*p_new) 2092 camera.SetFocalPoint(*pos) 2093 return camera 2094 2095 2096def camera_from_neuroglancer(state, zoom=300): 2097 """ 2098 Define a `vtkCamera` from a neuroglancer state dictionary. 2099 2100 Arguments: 2101 state: (dict) 2102 an neuroglancer state dictionary. 2103 zoom: (float) 2104 how much to multiply zoom by to get camera backoff distance 2105 default = 300 > ngl_zoom = 1 > 300 nm backoff distance. 2106 2107 Returns: 2108 `vtk.vtkCamera`, a vtk camera setup that matches this state. 2109 """ 2110 orient = state.get("perspectiveOrientation", [0.0, 0.0, 0.0, 1.0]) 2111 pzoom = state.get("perspectiveZoom", 10.0) 2112 position = state["navigation"]["pose"]["position"] 2113 pos_nm = np.array(position["voxelCoordinates"]) * position["voxelSize"] 2114 return camera_from_quaternion(pos_nm, orient, pzoom * zoom, ngl_correct=True) 2115 2116 2117def oriented_camera(center=(0, 0, 0), up_vector=(0, 1, 0), backoff_vector=(0, 0, 1), backoff=1.0): 2118 """ 2119 Generate a `vtkCamera` pointed at a specific location, 2120 oriented with a given up direction, set to a backoff. 2121 """ 2122 vup = np.array(up_vector) 2123 vup = vup / np.linalg.norm(vup) 2124 pt_backoff = center - backoff * np.array(backoff_vector) 2125 camera = vtk.vtkCamera() 2126 camera.SetFocalPoint(center[0], center[1], center[2]) 2127 camera.SetViewUp(vup[0], vup[1], vup[2]) 2128 camera.SetPosition(pt_backoff[0], pt_backoff[1], pt_backoff[2]) 2129 return camera 2130 2131 2132def camera_from_dict(camera, modify_inplace=None): 2133 """ 2134 Generate a `vtkCamera` from a dictionary. 2135 """ 2136 if modify_inplace: 2137 vcam = modify_inplace 2138 else: 2139 vcam = vtk.vtkCamera() 2140 2141 camera = dict(camera) # make a copy so input is not emptied by pop() 2142 cm_pos = camera.pop("position", camera.pop("pos", None)) 2143 cm_focal_point = camera.pop("focal_point", camera.pop("focalPoint", None)) 2144 cm_viewup = camera.pop("viewup", None) 2145 cm_distance = camera.pop("distance", None) 2146 cm_clipping_range = camera.pop("clipping_range", camera.pop("clippingRange", None)) 2147 cm_parallel_scale = camera.pop("parallel_scale", camera.pop("parallelScale", None)) 2148 cm_thickness = camera.pop("thickness", None) 2149 cm_view_angle = camera.pop("view_angle", camera.pop("viewAngle", None)) 2150 if len(camera.keys()) > 0: 2151 vedo.logger.warning(f"in camera_from_dict, key(s) not recognized: {camera.keys()}") 2152 if cm_pos is not None: vcam.SetPosition(cm_pos) 2153 if cm_focal_point is not None: vcam.SetFocalPoint(cm_focal_point) 2154 if cm_viewup is not None: vcam.SetViewUp(cm_viewup) 2155 if cm_distance is not None: vcam.SetDistance(cm_distance) 2156 if cm_clipping_range is not None: vcam.SetClippingRange(cm_clipping_range) 2157 if cm_parallel_scale is not None: vcam.SetParallelScale(cm_parallel_scale) 2158 if cm_thickness is not None: vcam.SetThickness(cm_thickness) 2159 if cm_view_angle is not None: vcam.SetViewAngle(cm_view_angle) 2160 return vcam 2161 2162 2163def vtkCameraToK3D(vtkcam): 2164 """ 2165 Convert a `vtkCamera` object into a 9-element list to be used by the K3D backend. 2166 2167 Output format is: 2168 `[posx,posy,posz, targetx,targety,targetz, upx,upy,upz]`. 2169 """ 2170 cpos = np.array(vtkcam.GetPosition()) 2171 kam = [cpos.tolist()] 2172 kam.append(vtkcam.GetFocalPoint()) 2173 kam.append(vtkcam.GetViewUp()) 2174 return np.array(kam).ravel() 2175 2176 2177def make_ticks(x0, x1, n=None, labels=None, digits=None, logscale=False, useformat=""): 2178 """ 2179 Generate numeric labels for the `[x0, x1]` range. 2180 2181 The format specifier could be expressed in the format: 2182 `:[[fill]align][sign][#][0][width][,][.precision][type]` 2183 2184 where, the options are: 2185 ``` 2186 fill = any character 2187 align = < | > | = | ^ 2188 sign = + | - | " " 2189 width = integer 2190 precision = integer 2191 type = b | c | d | e | E | f | F | g | G | n | o | s | x | X | % 2192 ``` 2193 2194 E.g.: useformat=":.2f" 2195 """ 2196 # Copyright M. Musy, 2021, license: MIT. 2197 # 2198 # useformat eg: ":.2f", check out: 2199 # https://mkaz.blog/code/python-string-format-cookbook/ 2200 # https://www.programiz.com/python-programming/methods/built-in/format 2201 2202 if x1 <= x0: 2203 # vedo.printc("Error in make_ticks(): x0 >= x1", x0,x1, c='r') 2204 return np.array([0.0, 1.0]), ["", ""] 2205 2206 ticks_str, ticks_float = [], [] 2207 baseline = (1, 2, 5, 10, 20, 50) 2208 2209 if logscale: 2210 if x0 <= 0 or x1 <= 0: 2211 vedo.logger.error("make_ticks: zero or negative range with log scale.") 2212 raise RuntimeError 2213 if n is None: 2214 n = int(abs(np.log10(x1) - np.log10(x0))) + 1 2215 x0, x1 = np.log10([x0, x1]) 2216 2217 if not n: 2218 n = 5 2219 2220 if labels is not None: 2221 # user is passing custom labels 2222 2223 ticks_float.append(0) 2224 ticks_str.append("") 2225 for tp, ts in labels: 2226 if tp == x1: 2227 continue 2228 ticks_str.append(str(ts)) 2229 tickn = lin_interpolate(tp, [x0, x1], [0, 1]) 2230 ticks_float.append(tickn) 2231 2232 else: 2233 # ..here comes one of the shortest and most painful pieces of code: 2234 # automatically choose the best natural axis subdivision based on multiples of 1,2,5 2235 dstep = (x1 - x0) / n # desired step size, begin of the nightmare 2236 2237 basestep = pow(10, np.floor(np.log10(dstep))) 2238 steps = np.array([basestep * i for i in baseline]) 2239 idx = (np.abs(steps - dstep)).argmin() 2240 s = steps[idx] # chosen step size 2241 2242 low_bound, up_bound = 0, 0 2243 if x0 < 0: 2244 low_bound = -pow(10, np.ceil(np.log10(-x0))) 2245 if x1 > 0: 2246 up_bound = pow(10, np.ceil(np.log10(x1))) 2247 2248 if low_bound < 0: 2249 if up_bound < 0: 2250 negaxis = np.arange(low_bound, int(up_bound / s) * s) 2251 else: 2252 if -low_bound / s > 1.0e06: 2253 return np.array([0.0, 1.0]), ["", ""] 2254 negaxis = np.arange(low_bound, 0, s) 2255 else: 2256 negaxis = np.array([]) 2257 2258 if up_bound > 0: 2259 if low_bound > 0: 2260 posaxis = np.arange(int(low_bound / s) * s, up_bound, s) 2261 else: 2262 if up_bound / s > 1.0e06: 2263 return np.array([0.0, 1.0]), ["", ""] 2264 posaxis = np.arange(0, up_bound, s) 2265 else: 2266 posaxis = np.array([]) 2267 2268 fulaxis = np.unique(np.clip(np.concatenate([negaxis, posaxis]), x0, x1)) 2269 # end of the nightmare 2270 2271 if useformat: 2272 sf = "{" + f"{useformat}" + "}" 2273 sas = "" 2274 for x in fulaxis: 2275 sas += sf.format(x) + " " 2276 elif digits is None: 2277 np.set_printoptions(suppress=True) # avoid zero precision 2278 sas = str(fulaxis).replace("[", "").replace("]", "") 2279 sas = sas.replace(".e", "e").replace("e+0", "e+").replace("e-0", "e-") 2280 np.set_printoptions(suppress=None) # set back to default 2281 else: 2282 sas = precision(fulaxis, digits, vrange=(x0, x1)) 2283 sas = sas.replace("[", "").replace("]", "").replace(")", "").replace(",", "") 2284 2285 sas2 = [] 2286 for s in sas.split(): 2287 if s.endswith("."): 2288 s = s[:-1] 2289 if s == "-0": 2290 s = "0" 2291 if digits is not None and "e" in s: 2292 s += " " # add space to terminate modifiers 2293 sas2.append(s) 2294 2295 for ts, tp in zip(sas2, fulaxis): 2296 if tp == x1: 2297 continue 2298 tickn = lin_interpolate(tp, [x0, x1], [0, 1]) 2299 ticks_float.append(tickn) 2300 if logscale: 2301 val = np.power(10, tp) 2302 if useformat: 2303 sf = "{" + f"{useformat}" + "}" 2304 ticks_str.append(sf.format(val)) 2305 else: 2306 if val >= 10: 2307 val = int(val + 0.5) 2308 else: 2309 val = round_to_digit(val, 2) 2310 ticks_str.append(str(val)) 2311 else: 2312 ticks_str.append(ts) 2313 2314 ticks_str.append("") 2315 ticks_float.append(1) 2316 ticks_float = np.array(ticks_float) 2317 return ticks_float, ticks_str 2318 2319 2320def grid_corners(i, nm, size, margin=0, yflip=True): 2321 """ 2322 Compute the 2 corners coordinates of the i-th box in a grid of shape n*m. 2323 The top-left square is square number 1. 2324 2325 Arguments: 2326 i : (int) 2327 input index of the desired grid square (to be used in `show(..., at=...)`). 2328 nm : (list) 2329 grid shape as (n,m). 2330 size : (list) 2331 total size of the grid along x and y. 2332 margin : (float) 2333 keep a small margin between boxes. 2334 yflip : (bool) 2335 y-coordinate points downwards 2336 2337 Returns: 2338 Two 2D points representing the bottom-left corner and the top-right corner 2339 of the `i`-nth box in the grid. 2340 2341 Example: 2342 ```python 2343 from vedo import * 2344 acts=[] 2345 n,m = 5,7 2346 for i in range(1, n*m + 1): 2347 c1,c2 = utils.grid_corners(i, [n,m], [1,1], 0.01) 2348 t = Text3D(i, (c1+c2)/2, c='k', s=0.02, justify='center').z(0.01) 2349 r = Rectangle(c1, c2, c=i) 2350 acts += [t,r] 2351 show(acts, axes=1).close() 2352 ``` 2353 ![](https://vedo.embl.es/images/feats/grid_corners.png) 2354 """ 2355 i -= 1 2356 n, m = nm 2357 sx, sy = size 2358 dx, dy = sx / n, sy / m 2359 nx = i % n 2360 ny = int((i - nx) / n) 2361 if yflip: 2362 ny = n - ny 2363 c1 = (dx * nx + margin, dy * ny + margin) 2364 c2 = (dx * (nx + 1) - margin, dy * (ny + 1) - margin) 2365 return np.array(c1), np.array(c2) 2366 2367 2368############################################################################ 2369# Trimesh support 2370# 2371# Install trimesh with: 2372# 2373# sudo apt install python3-rtree 2374# pip install rtree shapely 2375# conda install trimesh 2376# 2377# Check the example gallery in: examples/other/trimesh> 2378########################################################################### 2379def vedo2trimesh(mesh): 2380 """ 2381 Convert `vedo.mesh.Mesh` to `Trimesh.Mesh` object. 2382 """ 2383 if is_sequence(mesh): 2384 tms = [] 2385 for a in mesh: 2386 tms.append(vedo2trimesh(a)) 2387 return tms 2388 2389 from trimesh import Trimesh 2390 2391 tris = mesh.faces() 2392 carr = mesh.celldata["CellIndividualColors"] 2393 ccols = carr 2394 2395 points = mesh.points() 2396 varr = mesh.pointdata["VertexColors"] 2397 vcols = varr 2398 2399 if len(tris) == 0: 2400 tris = None 2401 2402 return Trimesh(vertices=points, faces=tris, face_colors=ccols, vertex_colors=vcols) 2403 2404 2405def trimesh2vedo(inputobj): 2406 """ 2407 Convert a `Trimesh` object to `vedo.Mesh` or `vedo.Assembly` object. 2408 """ 2409 if is_sequence(inputobj): 2410 vms = [] 2411 for ob in inputobj: 2412 vms.append(trimesh2vedo(ob)) 2413 return vms 2414 2415 inputobj_type = str(type(inputobj)) 2416 2417 if "Trimesh" in inputobj_type or "primitives" in inputobj_type: 2418 faces = inputobj.faces 2419 poly = buildPolyData(inputobj.vertices, faces) 2420 tact = vedo.Mesh(poly) 2421 if inputobj.visual.kind == "face": 2422 trim_c = inputobj.visual.face_colors 2423 else: 2424 trim_c = inputobj.visual.vertex_colors 2425 2426 if is_sequence(trim_c): 2427 if is_sequence(trim_c[0]): 2428 same_color = len(np.unique(trim_c, axis=0)) < 2 # all vtxs have same color 2429 2430 if same_color: 2431 tact.c(trim_c[0, [0, 1, 2]]).alpha(trim_c[0, 3]) 2432 else: 2433 if inputobj.visual.kind == "face": 2434 tact.cell_individual_colors(trim_c) 2435 return tact 2436 2437 if "PointCloud" in inputobj_type: 2438 2439 trim_cc, trim_al = "black", 1 2440 if hasattr(inputobj, "vertices_color"): 2441 trim_c = inputobj.vertices_color 2442 if len(trim_c) > 0: 2443 trim_cc = trim_c[:, [0, 1, 2]] / 255 2444 trim_al = trim_c[:, 3] / 255 2445 trim_al = np.sum(trim_al) / len(trim_al) # just the average 2446 return vedo.shapes.Points(inputobj.vertices, r=8, c=trim_cc, alpha=trim_al) 2447 2448 if "path" in inputobj_type: 2449 2450 lines = [] 2451 for e in inputobj.entities: 2452 # print('trimesh entity', e.to_dict()) 2453 l = vedo.shapes.Line(inputobj.vertices[e.points], c="k", lw=2) 2454 lines.append(l) 2455 return vedo.Assembly(lines) 2456 2457 return None 2458 2459 2460def vedo2meshlab(vmesh): 2461 """Convert a `vedo.Mesh` to a Meshlab object.""" 2462 try: 2463 import pymeshlab as mlab 2464 except RuntimeError: 2465 vedo.logger.error("Need pymeshlab to run:\npip install pymeshlab") 2466 2467 vertex_matrix = vmesh.points().astype(np.float64) 2468 2469 try: 2470 face_matrix = np.asarray(vmesh.faces(), dtype=np.float64) 2471 except: 2472 print("In vedo2meshlab, need to triangulate mesh first!") 2473 face_matrix = np.array(vmesh.clone().triangulate().faces(), dtype=np.float64) 2474 2475 v_normals_matrix = vmesh.normals(cells=False, recompute=False) 2476 if not v_normals_matrix.shape[0]: 2477 v_normals_matrix = np.empty((0, 3), dtype=np.float64) 2478 2479 f_normals_matrix = vmesh.normals(cells=True, recompute=False) 2480 if not f_normals_matrix.shape[0]: 2481 f_normals_matrix = np.empty((0, 3), dtype=np.float64) 2482 2483 v_color_matrix = vmesh.pointdata["RGBA"] 2484 if v_color_matrix is None: 2485 v_color_matrix = np.empty((0, 4), dtype=np.float64) 2486 else: 2487 v_color_matrix = v_color_matrix.astype(np.float64) / 255 2488 if v_color_matrix.shape[1] == 3: 2489 v_color_matrix = np.c_[ 2490 v_color_matrix, np.ones(v_color_matrix.shape[0], dtype=np.float64) 2491 ] 2492 2493 f_color_matrix = vmesh.celldata["RGBA"] 2494 if f_color_matrix is None: 2495 f_color_matrix = np.empty((0, 4), dtype=np.float64) 2496 else: 2497 f_color_matrix = f_color_matrix.astype(np.float64) / 255 2498 if f_color_matrix.shape[1] == 3: 2499 f_color_matrix = np.c_[ 2500 f_color_matrix, np.ones(f_color_matrix.shape[0], dtype=np.float64) 2501 ] 2502 2503 m = mlab.Mesh( 2504 vertex_matrix=vertex_matrix, 2505 face_matrix=face_matrix, 2506 v_normals_matrix=v_normals_matrix, 2507 f_normals_matrix=f_normals_matrix, 2508 v_color_matrix=v_color_matrix, 2509 f_color_matrix=f_color_matrix, 2510 ) 2511 2512 for k in vmesh.pointdata.keys(): 2513 data = vmesh.pointdata[k] 2514 if data is not None: 2515 if data.ndim == 1: # scalar 2516 m.add_vertex_custom_scalar_attribute(data.astype(np.float64), k) 2517 elif data.ndim == 2: # vectorial data 2518 m.add_vertex_custom_point_attribute(data.astype(np.float64), k) 2519 2520 for k in vmesh.celldata.keys(): 2521 data = vmesh.celldata[k] 2522 if data is not None: 2523 if data.ndim == 1: # scalar 2524 m.add_face_custom_scalar_attribute(data.astype(np.float64), k) 2525 elif data.ndim == 2: # vectorial data 2526 m.add_face_custom_point_attribute(data.astype(np.float64), k) 2527 2528 m.update_bounding_box() 2529 return m 2530 2531 2532def meshlab2vedo(mmesh): 2533 """Convert a Meshlab object to `vedo.Mesh`.""" 2534 inputtype = str(type(mmesh)) 2535 2536 if "MeshSet" in inputtype: 2537 mmesh = mmesh.current_mesh() 2538 2539 mpoints, mcells = mmesh.vertex_matrix(), mmesh.face_matrix() 2540 if len(mcells) > 0: 2541 polydata = buildPolyData(mpoints, mcells) 2542 else: 2543 polydata = buildPolyData(mpoints, None) 2544 2545 if mmesh.has_vertex_scalar(): 2546 parr = mmesh.vertex_scalar_array() 2547 parr_vtk = numpy_to_vtk(parr) 2548 parr_vtk.SetName("MeshLabScalars") 2549 x0, x1 = parr_vtk.GetRange() 2550 if x1 - x0: 2551 polydata.GetPointData().AddArray(parr_vtk) 2552 polydata.GetPointData().SetActiveScalars("MeshLabScalars") 2553 2554 if mmesh.has_face_scalar(): 2555 carr = mmesh.face_scalar_array() 2556 carr_vtk = numpy_to_vtk(carr) 2557 carr_vtk.SetName("MeshLabScalars") 2558 x0, x1 = carr_vtk.GetRange() 2559 if x1 - x0: 2560 polydata.GetCellData().AddArray(carr_vtk) 2561 polydata.GetCellData().SetActiveScalars("MeshLabScalars") 2562 2563 pnorms = mmesh.vertex_normal_matrix() 2564 if len(pnorms) > 0: 2565 polydata.GetPointData().SetNormals(numpy2vtk(pnorms)) 2566 2567 cnorms = mmesh.face_normal_matrix() 2568 if len(cnorms) > 0: 2569 polydata.GetCellData().SetNormals(numpy2vtk(cnorms)) 2570 return polydata 2571 2572 2573def open3d2vedo(o3d_mesh): 2574 """Convert `open3d.geometry.TriangleMesh` to a `vedo.Mesh`.""" 2575 m = vedo.Mesh([np.array(o3d_mesh.vertices), np.array(o3d_mesh.triangles)]) 2576 # TODO: could also check whether normals and color are present in 2577 # order to port with the above vertices/faces 2578 return m 2579 2580 2581def vedo2open3d(vedo_mesh): 2582 """ 2583 Return an `open3d.geometry.TriangleMesh` version of the current mesh. 2584 """ 2585 try: 2586 import open3d as o3d 2587 except RuntimeError: 2588 vedo.logger.error("Need open3d to run:\npip install open3d") 2589 2590 # create from numpy arrays 2591 o3d_mesh = o3d.geometry.TriangleMesh( 2592 vertices=o3d.utility.Vector3dVector(vedo_mesh.points()), 2593 triangles=o3d.utility.Vector3iVector(vedo_mesh.faces()), 2594 ) 2595 # TODO: need to add some if check here in case color and normals 2596 # info are not existing 2597 # o3d_mesh.vertex_colors = o3d.utility.Vector3dVector(vedo_mesh.pointdata["RGB"]/255) 2598 # o3d_mesh.vertex_normals= o3d.utility.Vector3dVector(vedo_mesh.pointdata["Normals"]) 2599 return o3d_mesh 2600 2601 2602def vtk_version_at_least(major, minor=0, build=0): 2603 """ 2604 Check the installed VTK version. 2605 2606 Return `True` if the requested VTK version is greater or equal to the actual VTK version. 2607 2608 Arguments: 2609 major : (int) 2610 Major version. 2611 minor : (int) 2612 Minor version. 2613 build : (int) 2614 Build version. 2615 """ 2616 needed_version = 10000000000 * int(major) + 100000000 * int(minor) + int(build) 2617 try: 2618 vtk_version_number = vtk.VTK_VERSION_NUMBER 2619 except AttributeError: # as error: 2620 ver = vtk.vtkVersion() 2621 vtk_version_number = ( 2622 10000000000 * ver.GetVTKMajorVersion() 2623 + 100000000 * ver.GetVTKMinorVersion() 2624 + ver.GetVTKBuildVersion() 2625 ) 2626 return vtk_version_number >= needed_version 2627 2628 2629def ctf2lut(tvobj, logscale=False): 2630 """Internal use.""" 2631 # build LUT from a color transfer function for tmesh or volume 2632 pr = tvobj.GetProperty() 2633 if not isinstance(pr, vtk.vtkVolumeProperty): 2634 return None 2635 ctf = pr.GetRGBTransferFunction() 2636 otf = pr.GetScalarOpacity() 2637 x0, x1 = tvobj.inputdata().GetScalarRange() 2638 cols, alphas = [], [] 2639 for x in np.linspace(x0, x1, 256): 2640 cols.append(ctf.GetColor(x)) 2641 alphas.append(otf.GetValue(x)) 2642 2643 if logscale: 2644 lut = vtk.vtkLogLookupTable() 2645 else: 2646 lut = vtk.vtkLookupTable() 2647 2648 lut.SetRange(x0, x1) 2649 lut.SetNumberOfTableValues(len(cols)) 2650 for i, col in enumerate(cols): 2651 r, g, b = col 2652 lut.SetTableValue(i, r, g, b, alphas[i]) 2653 lut.Build() 2654 return lut
90class OperationNode: 91 """ 92 Keep track of the operations which led to a final object. 93 """ 94 # https://www.graphviz.org/doc/info/shapes.html#html 95 # Mesh #e9c46a 96 # Follower #d9ed92 97 # Volume, UGrid #4cc9f0 98 # TetMesh #9e2a2b 99 # File #8a817c 100 # Picture #f28482 101 # Assembly #f08080 102 103 def __init__( 104 self, operation, parents=(), comment="", shape="none", c="#e9c46a", style="filled" 105 ): 106 """ 107 Keep track of the operations which led to a final object. 108 This allows to show the `pipeline` tree for any `vedo` object with e.g.: 109 110 ```python 111 from vedo import * 112 sp = Sphere() 113 sp.clean().subdivide() 114 sp.pipeline.show() 115 ``` 116 117 Arguments: 118 operation : (str, class) 119 descriptor label, if a class is passed then grab its name 120 parents : (list) 121 list of the parent classes the object comes from 122 comment : (str) 123 a second-line text description 124 shape : (str) 125 shape of the frame, check out [this link.](https://graphviz.org/doc/info/shapes.html) 126 c : (hex) 127 hex color 128 style : (str) 129 comma-separated list of styles 130 131 Example: 132 ```python 133 from vedo.utils import OperationNode 134 135 op_node1 = OperationNode("Operation1", c="lightblue") 136 op_node2 = OperationNode("Operation2") 137 op_node3 = OperationNode("Operation3", shape='diamond') 138 op_node4 = OperationNode("Operation4") 139 op_node5 = OperationNode("Operation5") 140 op_node6 = OperationNode("Result", c="lightgreen") 141 142 op_node3.add_parent(op_node1) 143 op_node4.add_parent(op_node1) 144 op_node3.add_parent(op_node2) 145 op_node5.add_parent(op_node2) 146 op_node6.add_parent(op_node3) 147 op_node6.add_parent(op_node5) 148 op_node6.add_parent(op_node1) 149 150 op_node6.show(orientation="TB") 151 ``` 152 ![](https://vedo.embl.es/images/feats/operation_node.png) 153 """ 154 if not vedo.settings.enable_pipeline: 155 return 156 157 if isinstance(operation, str): 158 self.operation = operation 159 else: 160 self.operation = operation.__class__.__name__ 161 self.operation_plain = str(self.operation) 162 163 pp = [] # filter out invalid stuff 164 for p in parents: 165 if hasattr(p, "pipeline"): 166 pp.append(p.pipeline) 167 self.parents = pp 168 169 if comment: 170 self.operation = f"<{self.operation}<BR/><SUB><I>{comment}</I></SUB>>" 171 172 self.dot = None 173 self.time = time.time() 174 self.shape = shape 175 self.style = style 176 self.color = c 177 self.counts = 0 178 179 def add_parent(self, parent): 180 self.parents.append(parent) 181 182 def _build_tree(self, dot): 183 dot.node( 184 str(id(self)), 185 label=self.operation, 186 shape=self.shape, 187 color=self.color, 188 style=self.style, 189 ) 190 for parent in self.parents: 191 if parent: 192 t = f"{self.time - parent.time: .1f}s" 193 dot.edge(str(id(parent)), str(id(self)), label=t) 194 parent._build_tree(dot) 195 196 def __repr__(self): 197 try: 198 from treelib import Tree 199 except ImportError: 200 vedo.logger.error( 201 "To use this functionality please install treelib:" "\n pip install treelib" 202 ) 203 return "" 204 205 def _build_tree(parent): 206 for par in parent.parents: 207 if par: 208 op = par.operation_plain 209 tree.create_node( 210 op, op + str(par.time), parent=parent.operation_plain + str(parent.time) 211 ) 212 _build_tree(par) 213 214 tree = Tree() 215 tree.create_node(self.operation_plain, self.operation_plain + str(self.time)) 216 _build_tree(self) 217 return tree.show(reverse=True, stdout=False) 218 219 def show(self, orientation="LR", popup=True): 220 """Show the graphviz output for the pipeline of this object""" 221 if not vedo.settings.enable_pipeline: 222 return 223 224 try: 225 from graphviz import Digraph 226 except ImportError: 227 vedo.logger.error("please install graphviz with command\n pip install graphviz") 228 return 229 230 # visualize the entire tree 231 dot = Digraph( 232 node_attr={"fontcolor": "#201010", "fontname": "Helvetica", "fontsize": "12"}, 233 edge_attr={"fontname": "Helvetica", "fontsize": "6", "arrowsize": "0.4"}, 234 ) 235 dot.attr(rankdir=orientation) 236 237 self.counts = 0 238 self._build_tree(dot) 239 self.dot = dot 240 dot.render(".vedo_pipeline_graphviz", view=popup)
Keep track of the operations which led to a final object.
103 def __init__( 104 self, operation, parents=(), comment="", shape="none", c="#e9c46a", style="filled" 105 ): 106 """ 107 Keep track of the operations which led to a final object. 108 This allows to show the `pipeline` tree for any `vedo` object with e.g.: 109 110 ```python 111 from vedo import * 112 sp = Sphere() 113 sp.clean().subdivide() 114 sp.pipeline.show() 115 ``` 116 117 Arguments: 118 operation : (str, class) 119 descriptor label, if a class is passed then grab its name 120 parents : (list) 121 list of the parent classes the object comes from 122 comment : (str) 123 a second-line text description 124 shape : (str) 125 shape of the frame, check out [this link.](https://graphviz.org/doc/info/shapes.html) 126 c : (hex) 127 hex color 128 style : (str) 129 comma-separated list of styles 130 131 Example: 132 ```python 133 from vedo.utils import OperationNode 134 135 op_node1 = OperationNode("Operation1", c="lightblue") 136 op_node2 = OperationNode("Operation2") 137 op_node3 = OperationNode("Operation3", shape='diamond') 138 op_node4 = OperationNode("Operation4") 139 op_node5 = OperationNode("Operation5") 140 op_node6 = OperationNode("Result", c="lightgreen") 141 142 op_node3.add_parent(op_node1) 143 op_node4.add_parent(op_node1) 144 op_node3.add_parent(op_node2) 145 op_node5.add_parent(op_node2) 146 op_node6.add_parent(op_node3) 147 op_node6.add_parent(op_node5) 148 op_node6.add_parent(op_node1) 149 150 op_node6.show(orientation="TB") 151 ``` 152 ![](https://vedo.embl.es/images/feats/operation_node.png) 153 """ 154 if not vedo.settings.enable_pipeline: 155 return 156 157 if isinstance(operation, str): 158 self.operation = operation 159 else: 160 self.operation = operation.__class__.__name__ 161 self.operation_plain = str(self.operation) 162 163 pp = [] # filter out invalid stuff 164 for p in parents: 165 if hasattr(p, "pipeline"): 166 pp.append(p.pipeline) 167 self.parents = pp 168 169 if comment: 170 self.operation = f"<{self.operation}<BR/><SUB><I>{comment}</I></SUB>>" 171 172 self.dot = None 173 self.time = time.time() 174 self.shape = shape 175 self.style = style 176 self.color = c 177 self.counts = 0
Keep track of the operations which led to a final object.
This allows to show the pipeline
tree for any vedo
object with e.g.:
from vedo import *
sp = Sphere()
sp.clean().subdivide()
sp.pipeline.show()
Arguments:
- operation : (str, class) descriptor label, if a class is passed then grab its name
- parents : (list) list of the parent classes the object comes from
- comment : (str) a second-line text description
- shape : (str) shape of the frame, check out this link.
- c : (hex) hex color
- style : (str) comma-separated list of styles
Example:
from vedo.utils import OperationNode op_node1 = OperationNode("Operation1", c="lightblue") op_node2 = OperationNode("Operation2") op_node3 = OperationNode("Operation3", shape='diamond') op_node4 = OperationNode("Operation4") op_node5 = OperationNode("Operation5") op_node6 = OperationNode("Result", c="lightgreen") op_node3.add_parent(op_node1) op_node4.add_parent(op_node1) op_node3.add_parent(op_node2) op_node5.add_parent(op_node2) op_node6.add_parent(op_node3) op_node6.add_parent(op_node5) op_node6.add_parent(op_node1) op_node6.show(orientation="TB")
219 def show(self, orientation="LR", popup=True): 220 """Show the graphviz output for the pipeline of this object""" 221 if not vedo.settings.enable_pipeline: 222 return 223 224 try: 225 from graphviz import Digraph 226 except ImportError: 227 vedo.logger.error("please install graphviz with command\n pip install graphviz") 228 return 229 230 # visualize the entire tree 231 dot = Digraph( 232 node_attr={"fontcolor": "#201010", "fontname": "Helvetica", "fontsize": "12"}, 233 edge_attr={"fontname": "Helvetica", "fontsize": "6", "arrowsize": "0.4"}, 234 ) 235 dot.attr(rankdir=orientation) 236 237 self.counts = 0 238 self._build_tree(dot) 239 self.dot = dot 240 dot.render(".vedo_pipeline_graphviz", view=popup)
Show the graphviz output for the pipeline of this object
244class ProgressBar: 245 """ 246 Class to print a progress bar. 247 """ 248 249 def __init__( 250 self, 251 start, 252 stop, 253 step=1, 254 c=None, 255 bold=True, 256 italic=False, 257 title="", 258 eta=True, 259 delay=0.0, 260 width=25, 261 char="\U00002501", 262 char_back="\U00002500", 263 ): 264 """ 265 Class to print a progress bar with optional text message. 266 267 Check out also function `progressbar()`. 268 269 Example: 270 ```python 271 import time 272 pb = ProgressBar(0,400, c='red') 273 for i in pb.range(): 274 time.sleep(0.1) 275 pb.print('some message') 276 ``` 277 ![](https://user-images.githubusercontent.com/32848391/51858823-ed1f4880-2335-11e9-8788-2d102ace2578.png) 278 """ 279 self.char = char 280 self.char_back = char_back 281 282 self.title = title + " " 283 if title: 284 self.title = " " + self.title 285 286 self.start = start 287 self.stop = stop 288 self.step = step 289 290 self.color = c 291 self.bold = bold 292 self.italic = italic 293 self.width = width 294 self.pbar = "" 295 self.percent = 0.0 296 self.percent_int = 0 297 self.eta = eta 298 self.delay = delay 299 300 self.t0 = time.time() 301 self._remaining = 1e10 302 303 self._update(0) 304 305 self._counts = 0 306 self._oldbar = "" 307 self._lentxt = 0 308 self._range = np.arange(start, stop, step) 309 310 def print(self, txt="", c=None): 311 """Print the progress bar with an optional message.""" 312 if not c: 313 c = self.color 314 315 self._update(self._counts + self.step) 316 317 if self.delay: 318 if time.time() - self.t0 < self.delay: 319 return 320 321 if self.pbar != self._oldbar: 322 self._oldbar = self.pbar 323 324 if self.eta and self._counts > 1: 325 326 tdenom = time.time() - self.t0 327 if tdenom: 328 vel = self._counts / tdenom 329 self._remaining = (self.stop - self._counts) / vel 330 else: 331 vel = 1 332 self._remaining = 0.0 333 334 if self._remaining > 60: 335 mins = int(self._remaining / 60) 336 secs = self._remaining - 60 * mins 337 mins = f"{mins}m" 338 secs = f"{int(secs + 0.5)}s " 339 else: 340 mins = "" 341 secs = f"{int(self._remaining + 0.5)}s " 342 343 vel = round(vel, 1) 344 eta = f"eta: {mins}{secs}({vel} it/s) " 345 if self._remaining < 0.5: 346 dt = time.time() - self.t0 347 if dt > 60: 348 mins = int(dt / 60) 349 secs = dt - 60 * mins 350 mins = f"{mins}m" 351 secs = f"{int(secs + 0.5)}s " 352 else: 353 mins = "" 354 secs = f"{int(dt + 0.5)}s " 355 eta = f"elapsed: {mins}{secs}({vel} it/s) " 356 txt = "" 357 else: 358 eta = "" 359 360 eraser = " " * self._lentxt + "\b" * self._lentxt 361 362 s = f"{self.pbar} {eraser}{eta}{txt}\r" 363 vedo.printc(s, c=c, bold=self.bold, italic=self.italic, end="") 364 if self.percent > 99.999: 365 print("") 366 367 self._lentxt = len(txt) 368 369 def range(self): 370 """Return the range iterator.""" 371 return self._range 372 373 def _update(self, counts): 374 if counts < self.start: 375 counts = self.start 376 elif counts > self.stop: 377 counts = self.stop 378 self._counts = counts 379 380 self.percent = (self._counts - self.start) * 100.0 381 382 delta = self.stop - self.start 383 if delta: 384 self.percent /= delta 385 else: 386 self.percent = 0.0 387 388 self.percent_int = int(round(self.percent)) 389 af = self.width - 2 390 nh = int(round(self.percent_int / 100 * af)) 391 pbar_background = "\x1b[2m" + self.char_back * (af - nh) 392 self.pbar = f"{self.title}{self.char * (nh-1)}{pbar_background}" 393 if self.percent < 100.0: 394 ps = f" {self.percent_int}%" 395 else: 396 ps = "" 397 self.pbar += ps
Class to print a progress bar.
249 def __init__( 250 self, 251 start, 252 stop, 253 step=1, 254 c=None, 255 bold=True, 256 italic=False, 257 title="", 258 eta=True, 259 delay=0.0, 260 width=25, 261 char="\U00002501", 262 char_back="\U00002500", 263 ): 264 """ 265 Class to print a progress bar with optional text message. 266 267 Check out also function `progressbar()`. 268 269 Example: 270 ```python 271 import time 272 pb = ProgressBar(0,400, c='red') 273 for i in pb.range(): 274 time.sleep(0.1) 275 pb.print('some message') 276 ``` 277 ![](https://user-images.githubusercontent.com/32848391/51858823-ed1f4880-2335-11e9-8788-2d102ace2578.png) 278 """ 279 self.char = char 280 self.char_back = char_back 281 282 self.title = title + " " 283 if title: 284 self.title = " " + self.title 285 286 self.start = start 287 self.stop = stop 288 self.step = step 289 290 self.color = c 291 self.bold = bold 292 self.italic = italic 293 self.width = width 294 self.pbar = "" 295 self.percent = 0.0 296 self.percent_int = 0 297 self.eta = eta 298 self.delay = delay 299 300 self.t0 = time.time() 301 self._remaining = 1e10 302 303 self._update(0) 304 305 self._counts = 0 306 self._oldbar = "" 307 self._lentxt = 0 308 self._range = np.arange(start, stop, step)
Class to print a progress bar with optional text message.
Check out also function progressbar()
.
Example:
import time pb = ProgressBar(0,400, c='red') for i in pb.range(): time.sleep(0.1) pb.print('some message')
310 def print(self, txt="", c=None): 311 """Print the progress bar with an optional message.""" 312 if not c: 313 c = self.color 314 315 self._update(self._counts + self.step) 316 317 if self.delay: 318 if time.time() - self.t0 < self.delay: 319 return 320 321 if self.pbar != self._oldbar: 322 self._oldbar = self.pbar 323 324 if self.eta and self._counts > 1: 325 326 tdenom = time.time() - self.t0 327 if tdenom: 328 vel = self._counts / tdenom 329 self._remaining = (self.stop - self._counts) / vel 330 else: 331 vel = 1 332 self._remaining = 0.0 333 334 if self._remaining > 60: 335 mins = int(self._remaining / 60) 336 secs = self._remaining - 60 * mins 337 mins = f"{mins}m" 338 secs = f"{int(secs + 0.5)}s " 339 else: 340 mins = "" 341 secs = f"{int(self._remaining + 0.5)}s " 342 343 vel = round(vel, 1) 344 eta = f"eta: {mins}{secs}({vel} it/s) " 345 if self._remaining < 0.5: 346 dt = time.time() - self.t0 347 if dt > 60: 348 mins = int(dt / 60) 349 secs = dt - 60 * mins 350 mins = f"{mins}m" 351 secs = f"{int(secs + 0.5)}s " 352 else: 353 mins = "" 354 secs = f"{int(dt + 0.5)}s " 355 eta = f"elapsed: {mins}{secs}({vel} it/s) " 356 txt = "" 357 else: 358 eta = "" 359 360 eraser = " " * self._lentxt + "\b" * self._lentxt 361 362 s = f"{self.pbar} {eraser}{eta}{txt}\r" 363 vedo.printc(s, c=c, bold=self.bold, italic=self.italic, end="") 364 if self.percent > 99.999: 365 print("") 366 367 self._lentxt = len(txt)
Print the progress bar with an optional message.
401def progressbar(iterable, c=None, bold=True, italic=False, title="", eta=True, width=25, delay=0.5): 402 """ 403 Function to print a progress bar with optional text message. 404 405 Use delay to set a minimum time before printing anything. 406 407 Example: 408 ```python 409 import time 410 for i in progressbar(range(100), c='red'): 411 time.sleep(0.1) 412 ``` 413 ![](https://user-images.githubusercontent.com/32848391/51858823-ed1f4880-2335-11e9-8788-2d102ace2578.png) 414 """ 415 try: 416 if is_number(iterable): 417 total = int(iterable) 418 iterable = range(total) 419 else: 420 total = len(iterable) 421 except TypeError: 422 iterable = list(iterable) 423 total = len(iterable) 424 425 pb = ProgressBar( 426 0, total, c=c, bold=bold, italic=italic, title=title, eta=eta, delay=delay, width=width 427 ) 428 for item in iterable: 429 pb.print() 430 yield item
Function to print a progress bar with optional text message.
Use delay to set a minimum time before printing anything.
Example:
import time for i in progressbar(range(100), c='red'): time.sleep(0.1)
515def geometry(obj, extent=None): 516 """ 517 Apply the `vtkGeometryFilter` to the input object. 518 This is a general-purpose filter to extract geometry (and associated data) 519 from any type of dataset. 520 This filter also may be used to convert any type of data to polygonal type. 521 The conversion process may be less than satisfactory for some 3D datasets. 522 For example, this filter will extract the outer surface of a volume 523 or structured grid dataset. 524 525 Returns a `vedo.Mesh` object. 526 527 Set `extent` as the `[xmin,xmax, ymin,ymax, zmin,zmax]` bounding box to clip data. 528 """ 529 gf = vtk.vtkGeometryFilter() 530 gf.SetInputData(obj) 531 if extent is not None: 532 gf.SetExtent(extent) 533 gf.Update() 534 return vedo.Mesh(gf.GetOutput())
Apply the vtkGeometryFilter
to the input object.
This is a general-purpose filter to extract geometry (and associated data)
from any type of dataset.
This filter also may be used to convert any type of data to polygonal type.
The conversion process may be less than satisfactory for some 3D datasets.
For example, this filter will extract the outer surface of a volume
or structured grid dataset.
Returns a vedo.Mesh
object.
Set extent
as the [xmin,xmax, ymin,ymax, zmin,zmax]
bounding box to clip data.
537def extract_cells_by_type(obj, types=()): 538 """ 539 Extract cells of a specified type from a vtk dataset. 540 541 Given an input `vtkDataSet` and a list of cell types, produce an output 542 containing only cells of the specified type(s). 543 544 Find [here](https://vtk.org/doc/nightly/html/vtkCellType_8h_source.html) 545 the list of possible cell types. 546 547 Return: 548 a `vtkDataSet` object which can be wrapped. 549 """ 550 ef = vtk.vtkExtractCellsByType() 551 try: 552 ef.SetInputData(obj.inputdata()) 553 except: 554 ef.SetInputData(obj) 555 556 for ct in types: 557 ef.AddCellType(ct) 558 ef.Update() 559 return ef.GetOutput()
Extract cells of a specified type from a vtk dataset.
Given an input vtkDataSet
and a list of cell types, produce an output
containing only cells of the specified type(s).
Find here the list of possible cell types.
Return:
a
vtkDataSet
object which can be wrapped.
739def is_sequence(arg): 740 """Check if the input is iterable.""" 741 if hasattr(arg, "strip"): 742 return False 743 if hasattr(arg, "__getslice__"): 744 return True 745 if hasattr(arg, "__iter__"): 746 return True 747 return False
Check if the input is iterable.
1048def lin_interpolate(x, rangeX, rangeY): 1049 """ 1050 Interpolate linearly the variable `x` in `rangeX` onto the new `rangeY`. 1051 If `x` is a 3D vector the linear weight is the distance to the two 3D `rangeX` vectors. 1052 1053 E.g. if `x` runs in `rangeX=[x0,x1]` and I want it to run in `rangeY=[y0,y1]` then 1054 1055 `y = lin_interpolate(x, rangeX, rangeY)` will interpolate `x` onto `rangeY`. 1056 1057 Examples: 1058 - [lin_interpolate.py](https://github.com/marcomusy/vedo/tree/master/examples/basic/lin_interpolate.py) 1059 1060 ![](https://vedo.embl.es/images/basic/linInterpolate.png) 1061 """ 1062 if is_sequence(x): 1063 x = np.asarray(x) 1064 x0, x1 = np.asarray(rangeX) 1065 y0, y1 = np.asarray(rangeY) 1066 dx = x1 - x0 1067 dxn = np.linalg.norm(dx) 1068 if not dxn: 1069 return y0 1070 s = np.linalg.norm(x - x0) / dxn 1071 t = np.linalg.norm(x - x1) / dxn 1072 st = s + t 1073 out = y0 * (t / st) + y1 * (s / st) 1074 1075 else: # faster 1076 1077 x0 = rangeX[0] 1078 dx = rangeX[1] - x0 1079 if not dx: 1080 return rangeY[0] 1081 s = (x - x0) / dx 1082 out = rangeY[0] * (1 - s) + rangeY[1] * s 1083 return out
Interpolate linearly the variable x
in rangeX
onto the new rangeY
.
If x
is a 3D vector the linear weight is the distance to the two 3D rangeX
vectors.
E.g. if x
runs in rangeX=[x0,x1]
and I want it to run in rangeY=[y0,y1]
then
y = lin_interpolate(x, rangeX, rangeY)
will interpolate x
onto rangeY
.
Examples:
1160def vector(x, y=None, z=0.0, dtype=np.float64): 1161 """ 1162 Return a 3D numpy array representing a vector. 1163 1164 If `y` is `None`, assume input is already in the form `[x,y,z]`. 1165 """ 1166 if y is None: # assume x is already [x,y,z] 1167 return np.asarray(x, dtype=dtype) 1168 return np.array([x, y, z], dtype=dtype)
Return a 3D numpy array representing a vector.
If y
is None
, assume input is already in the form [x,y,z]
.
1179def mag(v): 1180 """Get the magnitude of a vector or array of vectors.""" 1181 v = np.asarray(v) 1182 if v.ndim == 1: 1183 return np.linalg.norm(v) 1184 return np.linalg.norm(v, axis=1)
Get the magnitude of a vector or array of vectors.
1187def mag2(v): 1188 """Get the squared magnitude of a vector or array of vectors.""" 1189 v = np.asarray(v) 1190 if v.ndim == 1: 1191 return np.square(v).sum() 1192 return np.square(v).sum(axis=1)
Get the squared magnitude of a vector or array of vectors.
1171def versor(x, y=None, z=0.0, dtype=np.float64): 1172 """Return the unit vector. Input can be a list of vectors.""" 1173 v = vector(x, y, z, dtype) 1174 if isinstance(v[0], np.ndarray): 1175 return np.divide(v, mag(v)[:, None]) 1176 return v / mag(v)
Return the unit vector. Input can be a list of vectors.
1261def precision(x, p, vrange=None, delimiter="e"): 1262 """ 1263 Returns a string representation of `x` formatted to precision `p`. 1264 1265 Set `vrange` to the range in which x exists (to snap x to '0' if below precision). 1266 """ 1267 # Based on the webkit javascript implementation 1268 # `from here <https://code.google.com/p/webkit-mirror/source/browse/JavaScriptCore/kjs/number_object.cpp>`_, 1269 # and implemented by `randlet <https://github.com/randlet/to-precision>`_. 1270 # Modified for vedo by M.Musy 2020 1271 1272 if isinstance(x, str): # do nothing 1273 return x 1274 1275 if is_sequence(x): 1276 out = "(" 1277 nn = len(x) - 1 1278 for i, ix in enumerate(x): 1279 1280 try: 1281 if np.isnan(ix): 1282 return "NaN" 1283 except: 1284 # cannot handle list of list 1285 continue 1286 1287 out += precision(ix, p) 1288 if i < nn: 1289 out += ", " 1290 return out + ")" ############ <-- 1291 1292 if np.isnan(x): 1293 return "NaN" 1294 1295 x = float(x) 1296 1297 if x == 0.0 or (vrange is not None and abs(x) < vrange / pow(10, p)): 1298 return "0" 1299 1300 out = [] 1301 if x < 0: 1302 out.append("-") 1303 x = -x 1304 1305 e = int(math.log10(x)) 1306 tens = math.pow(10, e - p + 1) 1307 n = math.floor(x / tens) 1308 1309 if n < math.pow(10, p - 1): 1310 e = e - 1 1311 tens = math.pow(10, e - p + 1) 1312 n = math.floor(x / tens) 1313 1314 if abs((n + 1.0) * tens - x) <= abs(n * tens - x): 1315 n = n + 1 1316 1317 if n >= math.pow(10, p): 1318 n = n / 10.0 1319 e = e + 1 1320 1321 m = "%.*g" % (p, n) 1322 if e < -2 or e >= p: 1323 out.append(m[0]) 1324 if p > 1: 1325 out.append(".") 1326 out.extend(m[1:p]) 1327 out.append(delimiter) 1328 if e > 0: 1329 out.append("+") 1330 out.append(str(e)) 1331 elif e == (p - 1): 1332 out.append(m) 1333 elif e >= 0: 1334 out.append(m[: e + 1]) 1335 if e + 1 < len(m): 1336 out.append(".") 1337 out.extend(m[e + 1 :]) 1338 else: 1339 out.append("0.") 1340 out.extend(["0"] * -(e + 1)) 1341 out.append(m) 1342 return "".join(out)
Returns a string representation of x
formatted to precision p
.
Set vrange
to the range in which x exists (to snap x to '0' if below precision).
1214def round_to_digit(x, p): 1215 """Round a real number to the specified number of significant digits.""" 1216 if not x: 1217 return 0 1218 r = np.round(x, p - int(np.floor(np.log10(abs(x)))) - 1) 1219 if int(r) == r: 1220 return int(r) 1221 return r
Round a real number to the specified number of significant digits.
818def point_in_triangle(p, p1, p2, p3): 819 """ 820 Return True if a point is inside (or above/below) 821 a triangle defined by 3 points in space. 822 """ 823 p1 = np.array(p1) 824 u = p2 - p1 825 v = p3 - p1 826 n = np.cross(u, v) 827 w = p - p1 828 ln = np.dot(n, n) 829 if not ln: 830 return None # degenerate triangle 831 gamma = (np.dot(np.cross(u, w), n)) / ln 832 if 0 < gamma < 1: 833 beta = (np.dot(np.cross(w, v), n)) / ln 834 if 0 < beta < 1: 835 alpha = 1 - gamma - beta 836 if 0 < alpha < 1: 837 return True 838 return False
Return True if a point is inside (or above/below) a triangle defined by 3 points in space.
1002def point_line_distance(p, p1, p2): 1003 """ 1004 Compute the distance of a point to a line (not the segment) 1005 defined by `p1` and `p2`. 1006 """ 1007 return np.sqrt(vtk.vtkLine.DistanceToLine(p, p1, p2))
Compute the distance of a point to a line (not the segment)
defined by p1
and p2
.
1010def closest(point, points, n=1, return_ids=False, use_tree=False): 1011 """ 1012 Returns the distances and the closest point(s) to the given set of points. 1013 Needs `scipy.spatial` library. 1014 1015 Arguments: 1016 n : (int) 1017 the nr of closest points to return 1018 return_ids : (bool) 1019 return the ids instead of the points coordinates 1020 use_tree : (bool) 1021 build a `scipy.spatial.KDTree`. 1022 An already existing one can be passed to avoid rebuilding. 1023 """ 1024 from scipy.spatial import distance, KDTree 1025 1026 points = np.asarray(points) 1027 if n == 1: 1028 dists = distance.cdist([point], points) 1029 closest_idx = np.argmin(dists) 1030 else: 1031 if use_tree: 1032 if isinstance(use_tree, KDTree): # reuse 1033 tree = use_tree 1034 else: 1035 tree = KDTree(points) 1036 dists, closest_idx = tree.query([point], k=n) 1037 closest_idx = closest_idx[0] 1038 else: 1039 dists = distance.cdist([point], points) 1040 closest_idx = np.argsort(dists)[0][:n] 1041 if return_ids: 1042 return dists, closest_idx 1043 else: 1044 return dists, points[closest_idx]
Returns the distances and the closest point(s) to the given set of points.
Needs scipy.spatial
library.
Arguments:
- n : (int) the nr of closest points to return
- return_ids : (bool) return the ids instead of the points coordinates
- use_tree : (bool)
build a
scipy.spatial.KDTree
. An already existing one can be passed to avoid rebuilding.
1413def grep(filename, tag, column=None, first_occurrence_only=False): 1414 """Greps the line in a file that starts with a specific `tag` string inside the file.""" 1415 import re 1416 1417 with open(filename, "r", encoding="UTF-8") as afile: 1418 content = [] 1419 for line in afile: 1420 if re.search(tag, line): 1421 c = line.split() 1422 c[-1] = c[-1].replace("\n", "") 1423 if column is not None: 1424 c = c[column] 1425 content.append(c) 1426 if first_occurrence_only: 1427 break 1428 return content
Greps the line in a file that starts with a specific tag
string inside the file.
1431def print_info(obj): 1432 """Print information about a `vedo` object.""" 1433 1434 def _print_data(poly, c): 1435 ptdata = poly.GetPointData() 1436 cldata = poly.GetCellData() 1437 fldata = poly.GetFieldData() 1438 if ptdata.GetNumberOfArrays() + cldata.GetNumberOfArrays(): 1439 1440 for i in range(ptdata.GetNumberOfArrays()): 1441 name = ptdata.GetArrayName(i) 1442 if name and ptdata.GetArray(i): 1443 vedo.printc("pointdata".ljust(14) + ": ", c=c, bold=True, end="") 1444 try: 1445 tt, _ = array_types[ptdata.GetArray(i).GetDataType()] 1446 except: 1447 tt = "VTKTYPE" + str(ptdata.GetArray(i).GetDataType()) 1448 ncomp = ptdata.GetArray(i).GetNumberOfComponents() 1449 rng = ptdata.GetArray(i).GetRange() 1450 vedo.printc(f'"{name}" ({ncomp} {tt}),', c=c, bold=False, end="") 1451 vedo.printc( 1452 " range=(" + precision(rng[0], 3) + "," + precision(rng[1], 3) + ")", 1453 c=c, 1454 bold=False, 1455 ) 1456 1457 if ptdata.GetScalars(): 1458 vedo.printc("active scalars".ljust(14) + ": ", c=c, bold=True, end="") 1459 vedo.printc(ptdata.GetScalars().GetName(), "(pointdata) ", c=c, bold=False) 1460 1461 if ptdata.GetVectors(): 1462 vedo.printc("active vectors".ljust(14) + ": ", c=c, bold=True, end="") 1463 vedo.printc(ptdata.GetVectors().GetName(), "(pointdata) ", c=c, bold=False) 1464 1465 if ptdata.GetTensors(): 1466 vedo.printc("active tensors".ljust(14) + ": ", c=c, bold=True, end="") 1467 vedo.printc(ptdata.GetTensors().GetName(), "(pointdata) ", c=c, bold=False) 1468 1469 # same for cells 1470 for i in range(cldata.GetNumberOfArrays()): 1471 name = cldata.GetArrayName(i) 1472 if name and cldata.GetArray(i): 1473 vedo.printc("celldata".ljust(14) + ": ", c=c, bold=True, end="") 1474 try: 1475 tt, _ = array_types[cldata.GetArray(i).GetDataType()] 1476 except: 1477 tt = cldata.GetArray(i).GetDataType() 1478 ncomp = cldata.GetArray(i).GetNumberOfComponents() 1479 rng = cldata.GetArray(i).GetRange() 1480 vedo.printc(f'"{name}" ({ncomp} {tt}),', c=c, bold=False, end="") 1481 vedo.printc( 1482 " range=(" + precision(rng[0], 4) + "," + precision(rng[1], 4) + ")", 1483 c=c, 1484 bold=False, 1485 ) 1486 1487 if cldata.GetScalars(): 1488 vedo.printc("active scalars".ljust(14) + ": ", c=c, bold=True, end="") 1489 vedo.printc(cldata.GetScalars().GetName(), "(celldata)", c=c, bold=False) 1490 1491 if cldata.GetVectors(): 1492 vedo.printc("active vectors".ljust(14) + ": ", c=c, bold=True, end="") 1493 vedo.printc(cldata.GetVectors().GetName(), "(celldata)", c=c, bold=False) 1494 1495 for i in range(fldata.GetNumberOfArrays()): 1496 name = fldata.GetArrayName(i) 1497 if name and fldata.GetAbstractArray(i): 1498 arr = fldata.GetAbstractArray(i) 1499 vedo.printc("metadata".ljust(14) + ": ", c=c, bold=True, end="") 1500 ncomp = arr.GetNumberOfComponents() 1501 nvals = arr.GetNumberOfValues() 1502 vedo.printc(f'"{name}" ({ncomp} components, {nvals} values)', c=c, bold=False) 1503 1504 else: 1505 vedo.printc("mesh data".ljust(14) + ":", c=c, bold=True, end=" ") 1506 vedo.printc("no point or cell data is present.", c=c, bold=False) 1507 1508 ################################ 1509 def _printvtkactor(actor): 1510 1511 if not actor.GetPickable(): 1512 return 1513 1514 mapper = actor.GetMapper() 1515 if hasattr(actor, "polydata"): 1516 poly = actor.polydata() 1517 else: 1518 poly = mapper.GetInput() 1519 1520 pro = actor.GetProperty() 1521 pos = actor.GetPosition() 1522 bnds = actor.bounds() 1523 col = precision(pro.GetColor(), 3) 1524 alpha = pro.GetOpacity() 1525 npt = poly.GetNumberOfPoints() 1526 npl = poly.GetNumberOfPolys() 1527 nln = poly.GetNumberOfLines() 1528 1529 vedo.printc("Mesh/Points".ljust(70), c="g", bold=True, invert=True, dim=1, end="") 1530 1531 if hasattr(actor, "info") and "legend" in actor.info.keys() and actor.info["legend"]: 1532 vedo.printc("legend".ljust(14) + ": ", c="g", bold=True, end="") 1533 vedo.printc(actor.info["legend"], c="g", bold=False) 1534 else: 1535 print() 1536 1537 if hasattr(actor, "name") and actor.name: 1538 vedo.printc("name".ljust(14) + ": ", c="g", bold=True, end="") 1539 vedo.printc(actor.name, c="g", bold=False) 1540 1541 if hasattr(actor, "filename") and actor.filename: 1542 vedo.printc("file name".ljust(14) + ": ", c="g", bold=True, end="") 1543 vedo.printc(actor.filename, c="g", bold=False) 1544 1545 if not actor.GetMapper().GetScalarVisibility(): 1546 vedo.printc("color".ljust(14) + ": ", c="g", bold=True, end="") 1547 cname = vedo.colors.get_color_name(pro.GetColor()) 1548 vedo.printc(f"{cname}, rgb={col}, alpha={alpha}", c="g", bold=False) 1549 1550 if actor.GetBackfaceProperty(): 1551 bcol = actor.GetBackfaceProperty().GetDiffuseColor() 1552 cname = vedo.colors.get_color_name(bcol) 1553 vedo.printc("back color".ljust(14) + ": ", c="g", bold=True, end="") 1554 vedo.printc(f"{cname}, rgb={precision(bcol,3)}", c="g", bold=False) 1555 1556 vedo.printc("points".ljust(14) + ":", npt, c="g", bold=True) 1557 # ncl = poly.GetNumberOfCells() 1558 # vedo.printc("cells".ljust(14)+":", ncl, c="g", bold=True) 1559 vedo.printc("polygons".ljust(14) + ":", npl, c="g", bold=True) 1560 if nln: 1561 vedo.printc("lines".ljust(14) + ":", nln, c="g", bold=True) 1562 vedo.printc("position".ljust(14) + ":", pos, c="g", bold=True) 1563 1564 if hasattr(actor, "GetScale"): 1565 vedo.printc("scale".ljust(14) + ":", c="g", bold=True, end=" ") 1566 vedo.printc(precision(actor.GetScale(), 3), c="g", bold=False) 1567 1568 if hasattr(actor, "polydata") and actor.npoints: 1569 vedo.printc("center of mass".ljust(14) + ":", c="g", bold=True, end=" ") 1570 cm = tuple(actor.center_of_mass()) 1571 vedo.printc(precision(cm, 3), c="g", bold=False) 1572 1573 vedo.printc("average size".ljust(14) + ":", c="g", bold=True, end=" ") 1574 vedo.printc(precision(actor.average_size(), 6), c="g", bold=False) 1575 1576 vedo.printc("diagonal size".ljust(14) + ":", c="g", bold=True, end=" ") 1577 vedo.printc(precision(actor.diagonal_size(), 6), c="g", bold=False) 1578 1579 vedo.printc("bounds".ljust(14) + ":", c="g", bold=True, end=" ") 1580 bx1, bx2 = precision(bnds[0], 3), precision(bnds[1], 3) 1581 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="g", bold=False, end="") 1582 by1, by2 = precision(bnds[2], 3), precision(bnds[3], 3) 1583 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="g", bold=False, end="") 1584 bz1, bz2 = precision(bnds[4], 3), precision(bnds[5], 3) 1585 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="g", bold=False) 1586 1587 _print_data(poly, "g") 1588 1589 if hasattr(actor, "picked3d") and actor.picked3d is not None: 1590 idpt = actor.closest_point(actor.picked3d, return_point_id=True) 1591 idcell = actor.closest_point(actor.picked3d, return_cell_id=True) 1592 vedo.printc( 1593 "clicked point".ljust(14) + ":", 1594 precision(actor.picked3d, 6), 1595 f"pointID={idpt}, cellID={idcell}", 1596 c="g", 1597 bold=True, 1598 ) 1599 1600 if obj is None: 1601 return 1602 1603 if isinstance(obj, np.ndarray): 1604 cf = "y" 1605 vedo.printc("Numpy Array".ljust(70), c=cf, invert=True) 1606 vedo.printc(obj, c=cf) 1607 vedo.printc("shape".ljust(8) + ":", obj.shape, c=cf) 1608 vedo.printc("range".ljust(8) + f": ({np.min(obj)}, {np.max(obj)})", c=cf) 1609 vedo.printc("mean".ljust(8) + ":", np.mean(obj), c=cf) 1610 vedo.printc("std_dev".ljust(8) + ":", np.std(obj), c=cf) 1611 if len(obj.shape) >= 2: 1612 vedo.printc("Axis 0".ljust(8) + ":", c=cf, italic=1) 1613 vedo.printc("\tmin :", np.min(obj, axis=0), c=cf) 1614 vedo.printc("\tmax :", np.max(obj, axis=0), c=cf) 1615 vedo.printc("\tmean:", np.mean(obj, axis=0), c=cf) 1616 if obj.shape[1] > 3: 1617 vedo.printc("Axis 1".ljust(8) + ":", c=cf, italic=1) 1618 tmin = str(np.min(obj, axis=1).tolist()[:2]).replace("]", ", ...") 1619 tmax = str(np.max(obj, axis=1).tolist()[:2]).replace("]", ", ...") 1620 tmea = str(np.mean(obj, axis=1).tolist()[:2]).replace("]", ", ...") 1621 vedo.printc(f"\tmin : {tmin}", c=cf) 1622 vedo.printc(f"\tmax : {tmax}", c=cf) 1623 vedo.printc(f"\tmean: {tmea}", c=cf) 1624 1625 elif isinstance(obj, vedo.Points): 1626 _printvtkactor(obj) 1627 1628 elif isinstance(obj, vedo.Assembly): 1629 vedo.printc("Assembly".ljust(75), c="g", bold=True, invert=True) 1630 1631 pos = obj.GetPosition() 1632 bnds = obj.GetBounds() 1633 vedo.printc("position".ljust(14) + ": ", c="g", bold=True, end="") 1634 vedo.printc(pos, c="g", bold=False) 1635 1636 vedo.printc("bounds".ljust(14) + ": ", c="g", bold=True, end="") 1637 bx1, bx2 = precision(bnds[0], 3), precision(bnds[1], 3) 1638 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="g", bold=False, end="") 1639 by1, by2 = precision(bnds[2], 3), precision(bnds[3], 3) 1640 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="g", bold=False, end="") 1641 bz1, bz2 = precision(bnds[4], 3), precision(bnds[5], 3) 1642 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="g", bold=False) 1643 1644 cl = vtk.vtkPropCollection() 1645 obj.GetActors(cl) 1646 cl.InitTraversal() 1647 for _ in range(obj.GetNumberOfPaths()): 1648 act = vtk.vtkActor.SafeDownCast(cl.GetNextProp()) 1649 if isinstance(act, vtk.vtkActor): 1650 _printvtkactor(act) 1651 1652 elif isinstance(obj, vedo.TetMesh): 1653 cf = "m" 1654 vedo.printc("TetMesh".ljust(70), c=cf, bold=True, invert=True) 1655 pos = obj.GetPosition() 1656 bnds = obj.GetBounds() 1657 ug = obj.inputdata() 1658 vedo.printc("nr. of tetras".ljust(14) + ": ", c=cf, bold=True, end="") 1659 vedo.printc(ug.GetNumberOfCells(), c=cf, bold=False) 1660 vedo.printc("position".ljust(14) + ": ", c=cf, bold=True, end="") 1661 vedo.printc(pos, c=cf, bold=False) 1662 vedo.printc("bounds".ljust(14) + ": ", c=cf, bold=True, end="") 1663 bx1, bx2 = precision(bnds[0], 3), precision(bnds[1], 3) 1664 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c=cf, bold=False, end="") 1665 by1, by2 = precision(bnds[2], 3), precision(bnds[3], 3) 1666 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c=cf, bold=False, end="") 1667 bz1, bz2 = precision(bnds[4], 3), precision(bnds[5], 3) 1668 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c=cf, bold=False) 1669 _print_data(ug, cf) 1670 1671 elif isinstance(obj, (vedo.volume.Volume, vedo.volume.VolumeSlice)): 1672 vedo.printc("Volume".ljust(70), c="b", bold=True, invert=True) 1673 1674 img = obj.GetMapper().GetInput() 1675 vedo.printc("origin".ljust(14) + ": ", c="b", bold=True, end="") 1676 vedo.printc(precision(obj.origin(), 6), c="b", bold=False) 1677 1678 vedo.printc("center".ljust(14) + ": ", c="b", bold=True, end="") 1679 vedo.printc(precision(obj.center(), 6), c="b", bold=False) 1680 1681 vedo.printc("dimensions".ljust(14) + ": ", c="b", bold=True, end="") 1682 vedo.printc(img.GetDimensions(), c="b", bold=False) 1683 vedo.printc("spacing".ljust(14) + ": ", c="b", bold=True, end="") 1684 vedo.printc(precision(img.GetSpacing(), 6), c="b", bold=False) 1685 # vedo.printc("data dimension".ljust(14) + ": ", c="b", bold=True, end="") 1686 # vedo.printc(img.GetDataDimension(), c="b", bold=False) 1687 1688 vedo.printc("memory size".ljust(14) + ": ", c="b", bold=True, end="") 1689 vedo.printc(int(img.GetActualMemorySize() / 1024), "MB", c="b", bold=False) 1690 1691 vedo.printc("scalar #bytes".ljust(14) + ": ", c="b", bold=True, end="") 1692 vedo.printc(img.GetScalarSize(), c="b", bold=False) 1693 1694 bnds = obj.GetBounds() 1695 vedo.printc("bounds".ljust(14) + ": ", c="b", bold=True, end="") 1696 bx1, bx2 = precision(bnds[0], 4), precision(bnds[1], 4) 1697 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="b", bold=False, end="") 1698 by1, by2 = precision(bnds[2], 4), precision(bnds[3], 4) 1699 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="b", bold=False, end="") 1700 bz1, bz2 = precision(bnds[4], 4), precision(bnds[5], 4) 1701 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="b", bold=False) 1702 1703 vedo.printc("scalar range".ljust(14) + ": ", c="b", bold=True, end="") 1704 vedo.printc(img.GetScalarRange(), c="b", bold=False) 1705 1706 print_histogram(obj, horizontal=True, logscale=True, bins=8, height=15, c="b", bold=True) 1707 1708 elif isinstance(obj, vedo.Plotter) and obj.interactor: # dumps Plotter info 1709 axtype = { 1710 0: "(no axes)", 1711 1: "(three customizable gray grid walls)", 1712 2: "(cartesian axes from origin", 1713 3: "(positive range of cartesian axes from origin", 1714 4: "(axes triad at bottom left)", 1715 5: "(oriented cube at bottom left)", 1716 6: "(mark the corners of the bounding box)", 1717 7: "(3D ruler at each side of the cartesian axes)", 1718 8: "(the vtkCubeAxesActor object)", 1719 9: "(the bounding box outline)", 1720 10: "(circles of maximum bounding box range)", 1721 11: "(show a large grid on the x-y plane)", 1722 12: "(show polar axes)", 1723 13: "(simple ruler at the bottom of the window)", 1724 14: "(the vtkCameraOrientationWidget object)", 1725 } 1726 bns, totpt = [], 0 1727 for a in obj.actors: 1728 b = a.GetBounds() 1729 if a.GetBounds() is not None: 1730 if isinstance(a, vtk.vtkActor) and a.GetMapper(): 1731 totpt += a.GetMapper().GetInput().GetNumberOfPoints() 1732 bns.append(b) 1733 if len(bns) == 0: 1734 return 1735 vedo.printc("Plotter".ljust(70), invert=True, dim=1, c="c") 1736 otit = obj.title 1737 if not otit: 1738 otit = None 1739 vedo.printc("window title".ljust(14) + ":", otit, bold=False, c="c") 1740 vedo.printc( 1741 "window size".ljust(14) + ":", 1742 obj.window.GetSize(), 1743 "- full screen size:", 1744 obj.window.GetScreenSize(), 1745 bold=False, 1746 c="c", 1747 ) 1748 vedo.printc( 1749 "actv renderer".ljust(14) + ":", 1750 "nr.", 1751 obj.renderers.index(obj.renderer), 1752 f"(of {len(obj.renderers)} renderers)", 1753 bold=False, 1754 c="c", 1755 ) 1756 vedo.printc("nr. of actors".ljust(14) + ":", len(obj.actors), bold=False, c="c", end="") 1757 vedo.printc(" (" + str(totpt), "vertices)", bold=False, c="c") 1758 max_bns = np.max(bns, axis=0) 1759 min_bns = np.min(bns, axis=0) 1760 vedo.printc("max bounds".ljust(14) + ": ", c="c", bold=False, end="") 1761 bx1, bx2 = precision(min_bns[0], 3), precision(max_bns[1], 3) 1762 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="c", bold=False, end="") 1763 by1, by2 = precision(min_bns[2], 3), precision(max_bns[3], 3) 1764 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="c", bold=False, end="") 1765 bz1, bz2 = precision(min_bns[4], 3), precision(max_bns[5], 3) 1766 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="c", bold=False) 1767 if isinstance(obj.axes, dict): 1768 obj.axes = 1 1769 if obj.axes: 1770 vedo.printc("axes style".ljust(14) + ":", obj.axes, axtype[obj.axes], bold=False, c="c") 1771 1772 elif isinstance(obj, vedo.Picture): # dumps Picture info 1773 vedo.printc("Picture".ljust(70), c="y", bold=True, invert=True) 1774 try: 1775 # generate a print thumbnail 1776 width, height = obj.dimensions() 1777 w = 45 1778 h = int(height / width * (w - 1) * 0.5 + 0.5) 1779 img_arr = obj.clone().resize([w, h]).tonumpy() 1780 h, w = img_arr.shape[:2] 1781 for x in range(h): 1782 for y in range(w): 1783 pix = img_arr[x][y] 1784 r, g, b = pix[:3] 1785 print(f"\x1b[48;2;{r};{g};{b}m", end=" ") 1786 print("\x1b[0m") 1787 except: 1788 pass 1789 1790 img = obj.GetMapper().GetInput() 1791 pos = obj.GetPosition() 1792 vedo.printc("position".ljust(14) + ": ", c="y", bold=True, end="") 1793 vedo.printc(pos, c="y", bold=False) 1794 1795 vedo.printc("dimensions".ljust(14) + ": ", c="y", bold=True, end="") 1796 vedo.printc(obj.shape, c="y", bold=False) 1797 1798 vedo.printc("memory size".ljust(14) + ": ", c="y", bold=True, end="") 1799 vedo.printc(int(img.GetActualMemorySize()), "kB", c="y", bold=False) 1800 1801 bnds = obj.GetBounds() 1802 vedo.printc("bounds".ljust(14) + ": ", c="y", bold=True, end="") 1803 bx1, bx2 = precision(bnds[0], 3), precision(bnds[1], 3) 1804 vedo.printc("x=(" + bx1 + ", " + bx2 + ")", c="y", bold=False, end="") 1805 by1, by2 = precision(bnds[2], 3), precision(bnds[3], 3) 1806 vedo.printc(" y=(" + by1 + ", " + by2 + ")", c="y", bold=False, end="") 1807 bz1, bz2 = precision(bnds[4], 3), precision(bnds[5], 3) 1808 vedo.printc(" z=(" + bz1 + ", " + bz2 + ")", c="y", bold=False) 1809 1810 vedo.printc("intensty range".ljust(14) + ": ", c="y", bold=True, end="") 1811 vedo.printc(img.GetScalarRange(), c="y", bold=False) 1812 vedo.printc("level / window".ljust(14) + ": ", c="y", bold=True, end="") 1813 vedo.printc(obj.level(), "/", obj.window(), c="y", bold=False) 1814 1815 else: 1816 vedo.printc(str(type(obj)).ljust(70), invert=True) 1817 vedo.printc(obj)
Print information about a vedo
object.
2021def make_bands(inputlist, n): 2022 """ 2023 Group values of a list into bands of equal value, where 2024 `n` is the number of bands, a positive integer > 2. 2025 2026 Returns a binned list of the same length as the input. 2027 """ 2028 if n < 2: 2029 return inputlist 2030 vmin = np.min(inputlist) 2031 vmax = np.max(inputlist) 2032 bb = np.linspace(vmin, vmax, n, endpoint=0) 2033 dr = bb[1] - bb[0] 2034 bb += dr / 2 2035 tol = dr / 2 * 1.001 2036 newlist = [] 2037 for s in inputlist: 2038 for b in bb: 2039 if abs(s - b) < tol: 2040 newlist.append(b) 2041 break 2042 return np.array(newlist)
Group values of a list into bands of equal value, where
n
is the number of bands, a positive integer > 2.
Returns a binned list of the same length as the input.
1224def pack_spheres(bounds, radius): 1225 """ 1226 Packing spheres into a bounding box. 1227 Returns a numpy array of sphere centers. 1228 """ 1229 h = 0.8164965 / 2 1230 d = 0.8660254 1231 a = 0.288675135 1232 1233 if is_sequence(bounds): 1234 x0, x1, y0, y1, z0, z1 = bounds 1235 else: 1236 x0, x1, y0, y1, z0, z1 = bounds.bounds() 1237 1238 x = np.arange(x0, x1, radius) 1239 nul = np.zeros_like(x) 1240 nz = int((z1 - z0) / radius / h / 2 + 1.5) 1241 ny = int((y1 - y0) / radius / d + 1.5) 1242 1243 pts = [] 1244 for iz in range(nz): 1245 z = z0 + nul + iz * h * radius 1246 dx, dy, dz = [radius * 0.5, radius * a, iz * h * radius] 1247 for iy in range(ny): 1248 y = y0 + nul + iy * d * radius 1249 if iy % 2: 1250 xs = x 1251 else: 1252 xs = x + radius * 0.5 1253 if iz % 2: 1254 p = np.c_[xs, y, z] + [dx, dy, dz] 1255 else: 1256 p = np.c_[xs, y, z] + [0, 0, dz] 1257 pts += p.tolist() 1258 return np.array(pts)
Packing spheres into a bounding box. Returns a numpy array of sphere centers.
1371def spher2cart(rho, theta, phi): 1372 """3D Spherical to Cartesian coordinate conversion.""" 1373 st = np.sin(theta) 1374 sp = np.sin(phi) 1375 ct = np.cos(theta) 1376 cp = np.cos(phi) 1377 rst = rho * st 1378 x = rst * cp 1379 y = rst * sp 1380 z = rho * ct 1381 return np.array([x, y, z])
3D Spherical to Cartesian coordinate conversion.
1362def cart2spher(x, y, z): 1363 """3D Cartesian to Spherical coordinate conversion.""" 1364 hxy = np.hypot(x, y) 1365 rho = np.hypot(hxy, z) 1366 theta = np.arctan2(hxy, z) 1367 phi = np.arctan2(y, x) 1368 return np.array([rho, theta, phi])
3D Cartesian to Spherical coordinate conversion.
1384def cart2cyl(x, y, z): 1385 """3D Cartesian to Cylindrical coordinate conversion.""" 1386 rho = np.sqrt(x * x + y * y) 1387 theta = np.arctan2(y, x) 1388 return np.array([rho, theta, z])
3D Cartesian to Cylindrical coordinate conversion.
1391def cyl2cart(rho, theta, z): 1392 """3D Cylindrical to Cartesian coordinate conversion.""" 1393 x = rho * np.cos(theta) 1394 y = rho * np.sin(theta) 1395 return np.array([x, y, z])
3D Cylindrical to Cartesian coordinate conversion.
1398def cyl2spher(rho, theta, z): 1399 """3D Cylindrical to Spherical coordinate conversion.""" 1400 rhos = np.sqrt(rho * rho + z * z) 1401 phi = np.arctan2(rho, z) 1402 return np.array([rhos, phi, theta])
3D Cylindrical to Spherical coordinate conversion.
1405def spher2cyl(rho, theta, phi): 1406 """3D Spherical to Cylindrical coordinate conversion.""" 1407 rhoc = rho * np.sin(theta) 1408 z = rho * np.cos(theta) 1409 return np.array([rhoc, phi, z])
3D Spherical to Cylindrical coordinate conversion.
1347def cart2pol(x, y): 1348 """2D Cartesian to Polar coordinates conversion.""" 1349 theta = np.arctan2(y, x) 1350 rho = np.hypot(x, y) 1351 return np.array([rho, theta])
2D Cartesian to Polar coordinates conversion.
1354def pol2cart(rho, theta): 1355 """2D Polar to Cartesian coordinates conversion.""" 1356 x = rho * np.cos(theta) 1357 y = rho * np.sin(theta) 1358 return np.array([x, y])
2D Polar to Cartesian coordinates conversion.
785def humansort(alist): 786 """ 787 Sort in place a given list the way humans expect. 788 789 E.g. `['file11', 'file1'] -> ['file1', 'file11']` 790 791 .. warning:: input list is modified in-place by this function. 792 """ 793 import re 794 795 def alphanum_key(s): 796 # Turn a string into a list of string and number chunks. 797 # e.g. "z23a" -> ["z", 23, "a"] 798 def tryint(s): 799 if s.isdigit(): 800 return int(s) 801 return s 802 803 return [tryint(c) for c in re.split("([0-9]+)", s)] 804 805 alist.sort(key=alphanum_key) 806 return alist # NB: input list is modified
Sort in place a given list the way humans expect.
E.g. ['file11', 'file1'] -> ['file1', 'file11']
input list is modified in-place by this function.
1820def print_histogram( 1821 data, 1822 bins=10, 1823 height=10, 1824 logscale=False, 1825 minbin=0, 1826 horizontal=False, 1827 char="\u2588", 1828 c=None, 1829 bold=True, 1830 title="histogram", 1831 spacer="", 1832): 1833 """ 1834 Ascii histogram printing. 1835 1836 Input can be a `vedo.Volume` or `vedo.Mesh`. 1837 Returns the raw data before binning (useful when passing vtk objects). 1838 1839 Arguments: 1840 bins : (int) 1841 number of histogram bins 1842 height : (int) 1843 height of the histogram in character units 1844 logscale : (bool) 1845 use logscale for frequencies 1846 minbin : (int) 1847 ignore bins before minbin 1848 horizontal : (bool) 1849 show histogram horizontally 1850 char : (str) 1851 character to be used 1852 bold : (bool) 1853 use boldface 1854 title : (str) 1855 histogram title 1856 spacer : (str) 1857 horizontal spacer 1858 1859 Example: 1860 ```python 1861 from vedo import print_histogram 1862 import numpy as np 1863 d = np.random.normal(size=1000) 1864 data = print_histogram(d, c='blue', logscale=True, title='my scalars') 1865 data = print_histogram(d, c=1, horizontal=1) 1866 print(np.mean(data)) # data here is same as d 1867 ``` 1868 ![](https://vedo.embl.es/images/feats/print_histogram.png) 1869 """ 1870 # credits: http://pyinsci.blogspot.com/2009/10/ascii-histograms.html 1871 # adapted for vedo by M.Musy, 2019 1872 1873 if not horizontal: # better aspect ratio 1874 bins *= 2 1875 1876 isimg = isinstance(data, vtk.vtkImageData) 1877 isvol = isinstance(data, vtk.vtkVolume) 1878 if isimg or isvol: 1879 if isvol: 1880 img = data.imagedata() 1881 else: 1882 img = data 1883 dims = img.GetDimensions() 1884 nvx = min(100000, dims[0] * dims[1] * dims[2]) 1885 idxs = np.random.randint(0, min(dims), size=(nvx, 3)) 1886 data = [] 1887 for ix, iy, iz in idxs: 1888 d = img.GetScalarComponentAsFloat(ix, iy, iz, 0) 1889 data.append(d) 1890 elif isinstance(data, vtk.vtkActor): 1891 arr = data.polydata().GetPointData().GetScalars() 1892 if not arr: 1893 arr = data.polydata().GetCellData().GetScalars() 1894 if not arr: 1895 return None 1896 1897 data = vtk2numpy(arr) 1898 1899 try: 1900 h = np.histogram(data, bins=bins) 1901 except TypeError as e: 1902 vedo.logger.error(f"cannot compute histogram: {e}") 1903 return "" 1904 1905 if minbin: 1906 hi = h[0][minbin:-1] 1907 else: 1908 hi = h[0] 1909 1910 if char == "\U00002589" and horizontal: 1911 char = "\U00002586" 1912 1913 title = title.ljust(14) + ":" 1914 entrs = " entries=" + str(len(data)) 1915 if logscale: 1916 h0 = np.log10(hi + 1) 1917 maxh0 = int(max(h0) * 100) / 100 1918 title = title + entrs + " (logscale)" 1919 else: 1920 h0 = hi 1921 maxh0 = max(h0) 1922 title = title + entrs 1923 1924 def _v(): 1925 his = "" 1926 if title: 1927 his += title + "\n" 1928 bars = h0 / maxh0 * height 1929 for l in reversed(range(1, height + 1)): 1930 line = "" 1931 if l == height: 1932 line = "%s " % maxh0 1933 else: 1934 line = " |" + " " * (len(str(maxh0)) - 3) 1935 for c in bars: 1936 if c >= np.ceil(l): 1937 line += char 1938 else: 1939 line += " " 1940 line += "\n" 1941 his += line 1942 his += "%.2f" % h[1][0] + "." * (bins) + "%.2f" % h[1][-1] + "\n" 1943 return his 1944 1945 def _h(): 1946 his = "" 1947 if title: 1948 his += title + "\n" 1949 xl = ["%.2f" % n for n in h[1]] 1950 lxl = [len(l) for l in xl] 1951 bars = h0 / maxh0 * height 1952 his += spacer + " " * int(max(bars) + 2 + max(lxl)) + "%s\n" % maxh0 1953 for i, c in enumerate(bars): 1954 line = xl[i] + " " * int(max(lxl) - lxl[i]) + "| " + char * int(c) + "\n" 1955 his += spacer + line 1956 return his 1957 1958 if horizontal: 1959 height *= 2 1960 vedo.printc(_h(), c=c, bold=bold) 1961 else: 1962 vedo.printc(_v(), c=c, bold=bold) 1963 return data
Ascii histogram printing.
Input can be a vedo.Volume
or vedo.Mesh
.
Returns the raw data before binning (useful when passing vtk objects).
Arguments:
- bins : (int) number of histogram bins
- height : (int) height of the histogram in character units
- logscale : (bool) use logscale for frequencies
- minbin : (int) ignore bins before minbin
- horizontal : (bool) show histogram horizontally
- char : (str) character to be used
- bold : (bool) use boldface
- title : (str) histogram title
- spacer : (str) horizontal spacer
Example:
from vedo import print_histogram import numpy as np d = np.random.normal(size=1000) data = print_histogram(d, c='blue', logscale=True, title='my scalars') data = print_histogram(d, c=1, horizontal=1) print(np.mean(data)) # data here is same as d
2048def camera_from_quaternion(pos, quaternion, distance=10000, ngl_correct=True): 2049 """ 2050 Define a `vtkCamera` with a particular orientation. 2051 2052 Arguments: 2053 pos: (np.array, list, tuple) 2054 an iterator of length 3 containing the focus point of the camera 2055 quaternion: (np.array, list, tuple) 2056 a len(4) quaternion `(x,y,z,w)` describing the rotation of the camera 2057 such as returned by neuroglancer `x,y,z,w` all in `[0,1]` range 2058 distance: (float) 2059 the desired distance from pos to the camera (default = 10000 nm) 2060 2061 Returns: 2062 `vtk.vtkCamera`, a vtk camera setup according to these rules. 2063 """ 2064 camera = vtk.vtkCamera() 2065 # define the quaternion in vtk, note the swapped order 2066 # w,x,y,z instead of x,y,z,w 2067 quat_vtk = vtk.vtkQuaterniond(quaternion[3], quaternion[0], quaternion[1], quaternion[2]) 2068 # use this to define a rotation matrix in x,y,z 2069 # right handed units 2070 M = np.zeros((3, 3), dtype=np.float32) 2071 quat_vtk.ToMatrix3x3(M) 2072 # the default camera orientation is y up 2073 up = [0, 1, 0] 2074 # calculate default camera position is backed off in positive z 2075 pos = [0, 0, distance] 2076 2077 # set the camera rototation by applying the rotation matrix 2078 camera.SetViewUp(*np.dot(M, up)) 2079 # set the camera position by applying the rotation matrix 2080 camera.SetPosition(*np.dot(M, pos)) 2081 if ngl_correct: 2082 # neuroglancer has positive y going down 2083 # so apply these azimuth and roll corrections 2084 # to fix orientatins 2085 camera.Azimuth(-180) 2086 camera.Roll(180) 2087 2088 # shift the camera posiiton and focal position 2089 # to be centered on the desired location 2090 p = camera.GetPosition() 2091 p_new = np.array(p) + pos 2092 camera.SetPosition(*p_new) 2093 camera.SetFocalPoint(*pos) 2094 return camera
Define a vtkCamera
with a particular orientation.
Arguments:
- pos: (np.array, list, tuple) an iterator of length 3 containing the focus point of the camera
- quaternion: (np.array, list, tuple)
a len(4) quaternion
(x,y,z,w)
describing the rotation of the camera such as returned by neuroglancerx,y,z,w
all in[0,1]
range - distance: (float) the desired distance from pos to the camera (default = 10000 nm)
Returns:
vtk.vtkCamera
, a vtk camera setup according to these rules.
2097def camera_from_neuroglancer(state, zoom=300): 2098 """ 2099 Define a `vtkCamera` from a neuroglancer state dictionary. 2100 2101 Arguments: 2102 state: (dict) 2103 an neuroglancer state dictionary. 2104 zoom: (float) 2105 how much to multiply zoom by to get camera backoff distance 2106 default = 300 > ngl_zoom = 1 > 300 nm backoff distance. 2107 2108 Returns: 2109 `vtk.vtkCamera`, a vtk camera setup that matches this state. 2110 """ 2111 orient = state.get("perspectiveOrientation", [0.0, 0.0, 0.0, 1.0]) 2112 pzoom = state.get("perspectiveZoom", 10.0) 2113 position = state["navigation"]["pose"]["position"] 2114 pos_nm = np.array(position["voxelCoordinates"]) * position["voxelSize"] 2115 return camera_from_quaternion(pos_nm, orient, pzoom * zoom, ngl_correct=True)
Define a vtkCamera
from a neuroglancer state dictionary.
Arguments:
- state: (dict) an neuroglancer state dictionary.
- zoom: (float) how much to multiply zoom by to get camera backoff distance default = 300 > ngl_zoom = 1 > 300 nm backoff distance.
Returns:
vtk.vtkCamera
, a vtk camera setup that matches this state.
2118def oriented_camera(center=(0, 0, 0), up_vector=(0, 1, 0), backoff_vector=(0, 0, 1), backoff=1.0): 2119 """ 2120 Generate a `vtkCamera` pointed at a specific location, 2121 oriented with a given up direction, set to a backoff. 2122 """ 2123 vup = np.array(up_vector) 2124 vup = vup / np.linalg.norm(vup) 2125 pt_backoff = center - backoff * np.array(backoff_vector) 2126 camera = vtk.vtkCamera() 2127 camera.SetFocalPoint(center[0], center[1], center[2]) 2128 camera.SetViewUp(vup[0], vup[1], vup[2]) 2129 camera.SetPosition(pt_backoff[0], pt_backoff[1], pt_backoff[2]) 2130 return camera
Generate a vtkCamera
pointed at a specific location,
oriented with a given up direction, set to a backoff.
2380def vedo2trimesh(mesh): 2381 """ 2382 Convert `vedo.mesh.Mesh` to `Trimesh.Mesh` object. 2383 """ 2384 if is_sequence(mesh): 2385 tms = [] 2386 for a in mesh: 2387 tms.append(vedo2trimesh(a)) 2388 return tms 2389 2390 from trimesh import Trimesh 2391 2392 tris = mesh.faces() 2393 carr = mesh.celldata["CellIndividualColors"] 2394 ccols = carr 2395 2396 points = mesh.points() 2397 varr = mesh.pointdata["VertexColors"] 2398 vcols = varr 2399 2400 if len(tris) == 0: 2401 tris = None 2402 2403 return Trimesh(vertices=points, faces=tris, face_colors=ccols, vertex_colors=vcols)
Convert vedo.mesh.Mesh
to Trimesh.Mesh
object.
2406def trimesh2vedo(inputobj): 2407 """ 2408 Convert a `Trimesh` object to `vedo.Mesh` or `vedo.Assembly` object. 2409 """ 2410 if is_sequence(inputobj): 2411 vms = [] 2412 for ob in inputobj: 2413 vms.append(trimesh2vedo(ob)) 2414 return vms 2415 2416 inputobj_type = str(type(inputobj)) 2417 2418 if "Trimesh" in inputobj_type or "primitives" in inputobj_type: 2419 faces = inputobj.faces 2420 poly = buildPolyData(inputobj.vertices, faces) 2421 tact = vedo.Mesh(poly) 2422 if inputobj.visual.kind == "face": 2423 trim_c = inputobj.visual.face_colors 2424 else: 2425 trim_c = inputobj.visual.vertex_colors 2426 2427 if is_sequence(trim_c): 2428 if is_sequence(trim_c[0]): 2429 same_color = len(np.unique(trim_c, axis=0)) < 2 # all vtxs have same color 2430 2431 if same_color: 2432 tact.c(trim_c[0, [0, 1, 2]]).alpha(trim_c[0, 3]) 2433 else: 2434 if inputobj.visual.kind == "face": 2435 tact.cell_individual_colors(trim_c) 2436 return tact 2437 2438 if "PointCloud" in inputobj_type: 2439 2440 trim_cc, trim_al = "black", 1 2441 if hasattr(inputobj, "vertices_color"): 2442 trim_c = inputobj.vertices_color 2443 if len(trim_c) > 0: 2444 trim_cc = trim_c[:, [0, 1, 2]] / 255 2445 trim_al = trim_c[:, 3] / 255 2446 trim_al = np.sum(trim_al) / len(trim_al) # just the average 2447 return vedo.shapes.Points(inputobj.vertices, r=8, c=trim_cc, alpha=trim_al) 2448 2449 if "path" in inputobj_type: 2450 2451 lines = [] 2452 for e in inputobj.entities: 2453 # print('trimesh entity', e.to_dict()) 2454 l = vedo.shapes.Line(inputobj.vertices[e.points], c="k", lw=2) 2455 lines.append(l) 2456 return vedo.Assembly(lines) 2457 2458 return None
Convert a Trimesh
object to vedo.Mesh
or vedo.Assembly
object.
2461def vedo2meshlab(vmesh): 2462 """Convert a `vedo.Mesh` to a Meshlab object.""" 2463 try: 2464 import pymeshlab as mlab 2465 except RuntimeError: 2466 vedo.logger.error("Need pymeshlab to run:\npip install pymeshlab") 2467 2468 vertex_matrix = vmesh.points().astype(np.float64) 2469 2470 try: 2471 face_matrix = np.asarray(vmesh.faces(), dtype=np.float64) 2472 except: 2473 print("In vedo2meshlab, need to triangulate mesh first!") 2474 face_matrix = np.array(vmesh.clone().triangulate().faces(), dtype=np.float64) 2475 2476 v_normals_matrix = vmesh.normals(cells=False, recompute=False) 2477 if not v_normals_matrix.shape[0]: 2478 v_normals_matrix = np.empty((0, 3), dtype=np.float64) 2479 2480 f_normals_matrix = vmesh.normals(cells=True, recompute=False) 2481 if not f_normals_matrix.shape[0]: 2482 f_normals_matrix = np.empty((0, 3), dtype=np.float64) 2483 2484 v_color_matrix = vmesh.pointdata["RGBA"] 2485 if v_color_matrix is None: 2486 v_color_matrix = np.empty((0, 4), dtype=np.float64) 2487 else: 2488 v_color_matrix = v_color_matrix.astype(np.float64) / 255 2489 if v_color_matrix.shape[1] == 3: 2490 v_color_matrix = np.c_[ 2491 v_color_matrix, np.ones(v_color_matrix.shape[0], dtype=np.float64) 2492 ] 2493 2494 f_color_matrix = vmesh.celldata["RGBA"] 2495 if f_color_matrix is None: 2496 f_color_matrix = np.empty((0, 4), dtype=np.float64) 2497 else: 2498 f_color_matrix = f_color_matrix.astype(np.float64) / 255 2499 if f_color_matrix.shape[1] == 3: 2500 f_color_matrix = np.c_[ 2501 f_color_matrix, np.ones(f_color_matrix.shape[0], dtype=np.float64) 2502 ] 2503 2504 m = mlab.Mesh( 2505 vertex_matrix=vertex_matrix, 2506 face_matrix=face_matrix, 2507 v_normals_matrix=v_normals_matrix, 2508 f_normals_matrix=f_normals_matrix, 2509 v_color_matrix=v_color_matrix, 2510 f_color_matrix=f_color_matrix, 2511 ) 2512 2513 for k in vmesh.pointdata.keys(): 2514 data = vmesh.pointdata[k] 2515 if data is not None: 2516 if data.ndim == 1: # scalar 2517 m.add_vertex_custom_scalar_attribute(data.astype(np.float64), k) 2518 elif data.ndim == 2: # vectorial data 2519 m.add_vertex_custom_point_attribute(data.astype(np.float64), k) 2520 2521 for k in vmesh.celldata.keys(): 2522 data = vmesh.celldata[k] 2523 if data is not None: 2524 if data.ndim == 1: # scalar 2525 m.add_face_custom_scalar_attribute(data.astype(np.float64), k) 2526 elif data.ndim == 2: # vectorial data 2527 m.add_face_custom_point_attribute(data.astype(np.float64), k) 2528 2529 m.update_bounding_box() 2530 return m
Convert a vedo.Mesh
to a Meshlab object.
2533def meshlab2vedo(mmesh): 2534 """Convert a Meshlab object to `vedo.Mesh`.""" 2535 inputtype = str(type(mmesh)) 2536 2537 if "MeshSet" in inputtype: 2538 mmesh = mmesh.current_mesh() 2539 2540 mpoints, mcells = mmesh.vertex_matrix(), mmesh.face_matrix() 2541 if len(mcells) > 0: 2542 polydata = buildPolyData(mpoints, mcells) 2543 else: 2544 polydata = buildPolyData(mpoints, None) 2545 2546 if mmesh.has_vertex_scalar(): 2547 parr = mmesh.vertex_scalar_array() 2548 parr_vtk = numpy_to_vtk(parr) 2549 parr_vtk.SetName("MeshLabScalars") 2550 x0, x1 = parr_vtk.GetRange() 2551 if x1 - x0: 2552 polydata.GetPointData().AddArray(parr_vtk) 2553 polydata.GetPointData().SetActiveScalars("MeshLabScalars") 2554 2555 if mmesh.has_face_scalar(): 2556 carr = mmesh.face_scalar_array() 2557 carr_vtk = numpy_to_vtk(carr) 2558 carr_vtk.SetName("MeshLabScalars") 2559 x0, x1 = carr_vtk.GetRange() 2560 if x1 - x0: 2561 polydata.GetCellData().AddArray(carr_vtk) 2562 polydata.GetCellData().SetActiveScalars("MeshLabScalars") 2563 2564 pnorms = mmesh.vertex_normal_matrix() 2565 if len(pnorms) > 0: 2566 polydata.GetPointData().SetNormals(numpy2vtk(pnorms)) 2567 2568 cnorms = mmesh.face_normal_matrix() 2569 if len(cnorms) > 0: 2570 polydata.GetCellData().SetNormals(numpy2vtk(cnorms)) 2571 return polydata
Convert a Meshlab object to vedo.Mesh
.
2582def vedo2open3d(vedo_mesh): 2583 """ 2584 Return an `open3d.geometry.TriangleMesh` version of the current mesh. 2585 """ 2586 try: 2587 import open3d as o3d 2588 except RuntimeError: 2589 vedo.logger.error("Need open3d to run:\npip install open3d") 2590 2591 # create from numpy arrays 2592 o3d_mesh = o3d.geometry.TriangleMesh( 2593 vertices=o3d.utility.Vector3dVector(vedo_mesh.points()), 2594 triangles=o3d.utility.Vector3iVector(vedo_mesh.faces()), 2595 ) 2596 # TODO: need to add some if check here in case color and normals 2597 # info are not existing 2598 # o3d_mesh.vertex_colors = o3d.utility.Vector3dVector(vedo_mesh.pointdata["RGB"]/255) 2599 # o3d_mesh.vertex_normals= o3d.utility.Vector3dVector(vedo_mesh.pointdata["Normals"]) 2600 return o3d_mesh
Return an open3d.geometry.TriangleMesh
version of the current mesh.
2574def open3d2vedo(o3d_mesh): 2575 """Convert `open3d.geometry.TriangleMesh` to a `vedo.Mesh`.""" 2576 m = vedo.Mesh([np.array(o3d_mesh.vertices), np.array(o3d_mesh.triangles)]) 2577 # TODO: could also check whether normals and color are present in 2578 # order to port with the above vertices/faces 2579 return m
Convert open3d.geometry.TriangleMesh
to a vedo.Mesh
.
458def vtk2numpy(varr): 459 """Convert a `vtkDataArray`, `vtkIdList` or `vtTransform` into a numpy array.""" 460 if isinstance(varr, vtk.vtkIdList): 461 return np.array([varr.GetId(i) for i in range(varr.GetNumberOfIds())]) 462 elif isinstance(varr, vtk.vtkBitArray): 463 carr = vtk.vtkCharArray() 464 carr.DeepCopy(varr) 465 varr = carr 466 elif isinstance(varr, vtk.vtkHomogeneousTransform): 467 try: 468 varr = varr.GetMatrix() 469 except AttributeError: 470 pass 471 n = 4 472 M = [[varr.GetElement(i, j) for j in range(n)] for i in range(n)] 473 return np.array(M) 474 475 return vtk_to_numpy(varr)
Convert a vtkDataArray
, vtkIdList
or vtTransform
into a numpy array.
434def numpy2vtk(arr, dtype=None, deep=True, name=""): 435 """ 436 Convert a numpy array into a `vtkDataArray`. 437 Use `dtype='id'` for `vtkIdTypeArray` objects. 438 """ 439 # https://github.com/Kitware/VTK/blob/master/Wrapping/Python/vtkmodules/util/numpy_support.py 440 if arr is None: 441 return None 442 443 arr = np.ascontiguousarray(arr) 444 445 if dtype == "id": 446 varr = numpy_to_vtkIdTypeArray(arr.astype(np.int64), deep=deep) 447 elif dtype: 448 varr = numpy_to_vtk(arr.astype(dtype), deep=deep) 449 else: 450 # let numpy_to_vtk() decide what is best type based on arr type 451 varr = numpy_to_vtk(arr, deep=deep) 452 453 if name: 454 varr.SetName(name) 455 return varr
Convert a numpy array into a vtkDataArray
.
Use dtype='id'
for vtkIdTypeArray
objects.
1086def get_uv(p, x, v): 1087 """ 1088 Obtain the texture uv-coords of a point p belonging to a face that has point 1089 coordinates (x0, x1, x2) with the corresponding uv-coordinates v=(v0, v1, v2). 1090 All p and x0,x1,x2 are 3D-vectors, while v are their 2D uv-coordinates. 1091 1092 Example: 1093 ```python 1094 from vedo import * 1095 1096 pic = Picture(dataurl+"coloured_cube_faces.jpg") 1097 cb = Mesh(dataurl+"coloured_cube.obj").lighting("off").texture(pic) 1098 1099 cbpts = cb.points() 1100 faces = cb.faces() 1101 uv = cb.pointdata["Material"] 1102 1103 pt = [-0.2, 0.75, 2] 1104 pr = cb.closest_point(pt) 1105 1106 idface = cb.closest_point(pt, return_cell_id=True) 1107 idpts = faces[idface] 1108 uv_face = uv[idpts] 1109 1110 uv_pr = utils.get_uv(pr, cbpts[idpts], uv_face) 1111 print("interpolated uv =", uv_pr) 1112 1113 sx, sy = pic.dimensions() 1114 i_interp_uv = uv_pr * [sy, sx] 1115 ix, iy = i_interp_uv.astype(int) 1116 mpic = pic.tomesh() 1117 rgba = mpic.pointdata["RGBA"].reshape(sy, sx, 3) 1118 print("color =", rgba[ix, iy]) 1119 1120 show( 1121 [[cb, Point(pr), cb.labels("Material")], 1122 [pic, Point(i_interp_uv)]], 1123 N=2, axes=1, sharecam=False, 1124 ).close() 1125 ``` 1126 ![](https://vedo.embl.es/images/feats/utils_get_uv.png) 1127 """ 1128 # Vector vp=p-x0 is representable as alpha*s + beta*t, 1129 # where s = x1-x0 and t = x2-x0, in matrix form 1130 # vp = [alpha, beta] . matrix(s,t) 1131 # M = matrix(s,t) is 2x3 matrix, so (alpha, beta) can be found by 1132 # inverting any of its minor A with non-zero determinant. 1133 # Once found, uv-coords of p are vt0 + alpha (vt1-v0) + beta (vt2-v0) 1134 1135 p = np.asarray(p) 1136 x0, x1, x2 = np.asarray(x)[:3] 1137 vt0, vt1, vt2 = np.asarray(v)[:3] 1138 1139 s = x1 - x0 1140 t = x2 - x0 1141 vs = vt1 - vt0 1142 vt = vt2 - vt0 1143 vp = p - x0 1144 1145 # finding a minor with independent rows 1146 M = np.matrix([s, t]) 1147 mnr = [0, 1] 1148 A = M[:, mnr] 1149 if np.abs(np.linalg.det(A)) < 0.000001: 1150 mnr = [0, 2] 1151 A = M[:, mnr] 1152 if np.abs(np.linalg.det(A)) < 0.000001: 1153 mnr = [1, 2] 1154 A = M[:, mnr] 1155 Ainv = np.linalg.inv(A) 1156 alpha_beta = vp[mnr].dot(Ainv) # [alpha, beta] 1157 return np.asarray(vt0 + alpha_beta.dot(np.matrix([vs, vt])))[0]
Obtain the texture uv-coords of a point p belonging to a face that has point coordinates (x0, x1, x2) with the corresponding uv-coordinates v=(v0, v1, v2). All p and x0,x1,x2 are 3D-vectors, while v are their 2D uv-coordinates.
Example:
from vedo import * pic = Picture(dataurl+"coloured_cube_faces.jpg") cb = Mesh(dataurl+"coloured_cube.obj").lighting("off").texture(pic) cbpts = cb.points() faces = cb.faces() uv = cb.pointdata["Material"] pt = [-0.2, 0.75, 2] pr = cb.closest_point(pt) idface = cb.closest_point(pt, return_cell_id=True) idpts = faces[idface] uv_face = uv[idpts] uv_pr = utils.get_uv(pr, cbpts[idpts], uv_face) print("interpolated uv =", uv_pr) sx, sy = pic.dimensions() i_interp_uv = uv_pr * [sy, sx] ix, iy = i_interp_uv.astype(int) mpic = pic.tomesh() rgba = mpic.pointdata["RGBA"].reshape(sy, sx, 3) print("color =", rgba[ix, iy]) show( [[cb, Point(pr), cb.labels("Material")], [pic, Point(i_interp_uv)]], N=2, axes=1, sharecam=False, ).close()