class GanyScene(AppComponent): """Canvas for 3D surface mesh interactive plotting based on ipygany.""" name = '3D Scene' def setup(self): vertices, triangle_indices = self.dataset._widgets.to_unstructured_mesh() elev_da = self.dataset._widgets.elevation elev_min = elev_da.min() elev_max = elev_da.max() elev_arr = self.dataset._widgets.current_elevation.values data = { 'color': [Component(name='value', array=elev_arr, min=elev_min, max=elev_max)], 'warp': [Component(name='value', array=elev_arr, min=elev_min, max=elev_max)], } self.polymesh = PolyMesh(vertices=vertices, triangle_indices=triangle_indices, data=data) self.isocolor = IsoColor( self.polymesh, input=('color', 'value'), min=elev_min, max=elev_max ) self.warp = WarpByScalar(self.isocolor, input='warp', factor=1) self.scene = Scene([self.warp]) return self.scene def redraw_isocolor_warp(self): """Trigger scene redraw if data slice has been updated.""" new_warp_array = self.dataset._widgets.current_elevation.values new_color_array = self.dataset._widgets.current_color.values with self.scene.hold_sync(): self.polymesh[('color', 'value')].array = new_color_array self.polymesh[('warp', 'value')].array = new_warp_array def reset_isocolor_limits(self, step=False): """Resets color limits to data range. Parameters ---------- step : bool If true, resets the color range to the range of the data that is shown in the current scene. Otherwise (default), resets the color range to the whole data range. """ if step: da = self.dataset._widgets.current_color else: da = self.dataset._widgets.color with self.scene.hold_sync(): self.isocolor.min = da.min() self.isocolor.max = da.max() @property def linkable_traits(self): return [(self.scene, 'camera')]
def show_ipygany(plotter, return_viewer, height=None, width=None): """Show an ipygany scene.""" # convert each mesh in the plotter to an ipygany scene actors = plotter.renderer._actors meshes = [] for actor in actors.values(): ipygany_obj = ipygany_block_from_actor(actor) if ipygany_obj is not None: meshes.append(ipygany_obj) bc_color = color_float_to_hex(*plotter.background_color) scene = Scene(meshes, background_color=bc_color, camera=ipygany_camera_from_plotter(plotter)) # optionally size of the plotter if height is not None: scene.layout.height = f'{height}' if width is not None: scene.layout.width = f'{width}' cbar = None if len(plotter.scalar_bars): for mesh in meshes: if isinstance(mesh, ipygany.IsoColor): cbar = ipygany.ColorBar(mesh) colored_mesh = mesh break # Simply return the scene if return_viewer: return scene if cbar is not None: # Colormap choice widget colormap_dd = Dropdown(options=colormaps, description='Colormap:') jslink((colored_mesh, 'colormap'), (colormap_dd, 'index')) # sensible colorbar maximum width, or else it looks bad when # window is large. cbar.layout.max_width = '500px' cbar.layout.min_height = '50px' # stop from getting squished # cbar.layout.height = '20%' # stop from getting squished # cbar.layout.max_height = '' # Create a slider that will dynamically change the boundaries of the colormap # colormap_slider_range = FloatRangeSlider(value=[height_min, height_max], # min=height_min, max=height_max, # step=(height_max - height_min) / 100.) # jslink((colored_mesh, 'range'), (colormap_slider_range, 'value')) # create app title = HTML(value=f'<h3>{list(plotter.scalar_bars.keys())[0]}</h3>') legend = VBox((title, colormap_dd, cbar)) scene = AppLayout(center=scene, footer=legend, pane_heights=[0, 0, '150px']) display.display_html(scene)
def show_dem3d(self, dem): nr, nc = len(dem.y), len(dem.x) triangle_indices = np.empty((nr - 1, nc - 1, 2, 3), dtype='uint32') r = np.arange(nr * nc, dtype='uint32').reshape(nr, nc) triangle_indices[:, :, 0, 0] = r[:-1, :-1] triangle_indices[:, :, 1, 0] = r[:-1, 1:] triangle_indices[:, :, 0, 1] = r[:-1, 1:] triangle_indices[:, :, 1, 1] = r[1:, 1:] triangle_indices[:, :, :, 2] = r[1:, :-1, None] triangle_indices.shape = (-1, 3) height_component = Component(name='value', array=self.alt) mesh = PolyMesh(vertices=self.vertices, triangle_indices=triangle_indices, data={'height': [height_component]}) colored_mesh = IsoColor(mesh, input='height', min=np.min(self.alt), max=np.max(self.alt)) warped_mesh = Warp(colored_mesh, input=(0, 0, ('height', 'value')), factor=0) warp_slider = FloatSlider(min=0, max=10, value=0, description='Vertical exaggeration', style={'description_width': 'initial'}) jslink((warped_mesh, 'factor'), (warp_slider, 'value')) self.dem3d.clear_output() with self.dem3d: display(VBox((Scene([warped_mesh]), warp_slider)))
def render_ipyany_scene(visualize: Visualize, off_screen_file=False): from ipygany import Scene meshes = [] for obj in visualize.objects: meshes.append(obj.convert_to_ipygany_mesh()) return Scene(meshes)
def setup(self): vertices, triangle_indices = self.dataset._widgets.to_unstructured_mesh() elev_da = self.dataset._widgets.elevation elev_min = elev_da.min() elev_max = elev_da.max() elev_arr = self.dataset._widgets.current_elevation.values data = { 'color': [Component(name='value', array=elev_arr, min=elev_min, max=elev_max)], 'warp': [Component(name='value', array=elev_arr, min=elev_min, max=elev_max)], } self.polymesh = PolyMesh(vertices=vertices, triangle_indices=triangle_indices, data=data) self.isocolor = IsoColor( self.polymesh, input=('color', 'value'), min=elev_min, max=elev_max ) self.warp = WarpByScalar(self.isocolor, input='warp', factor=1) self.scene = Scene([self.warp]) return self.scene
colored_mesh = IsoColor(mesh, input=('S', 'YY'), min=sigmayy_min, max=sigmayy_max) # Create a slider that will dynamically change the boundaries of the colormap colormap_slider_range = FloatRangeSlider(value=[sigmayy_min, sigmayy_max], min=sigmayy_min, max=sigmayy_max, step=(sigmayy_max - sigmayy_min) / 100.) jslink((colored_mesh, 'range'), (colormap_slider_range, 'value')) # Create a colorbar widget colorbar = ColorBar(colored_mesh) # Colormap choice widget colormap = Dropdown(options=colormaps, description='colormap:') jslink((colored_mesh, 'colormap'), (colormap, 'index')) AppLayout( header=Scene([colored_mesh]), left_sidebar=VBox((colormap, colormap_slider_range)), right_sidebar=(colorbar), pane_widths=[1, 0, 1], pane_heights=['80%', '20%', 0], footer=None, )
#!/usr/bin/env python # coding: utf-8 # In[1]: from ipygany import Scene, TetraMesh mesh = TetraMesh.from_vtk('benchmarks/000-tuning-fork/fork.vtk') scene = Scene([mesh]) scene
def getNotebookBackend(actors2show, zoom, viewup): vp = settings.plotter_instance if zoom == 'tight': zoom=1 # disable it if isinstance(vp.shape, str) or sum(vp.shape) > 2: colors.printc("Multirendering is not supported in jupyter.", c=1) return #################################################################################### # https://github.com/InsightSoftwareConsortium/itkwidgets # /blob/master/itkwidgets/widget_viewer.py if 'itk' in settings.notebookBackend: from itkwidgets import view settings.notebook_plotter = view(actors=actors2show, cmap='jet', ui_collapsed=True, gradient_opacity=False) #################################################################################### elif settings.notebookBackend == 'k3d': try: import k3d # https://github.com/K3D-tools/K3D-jupyter except: print("Cannot find k3d, install with: pip install k3d") return actors2show2 = [] for ia in actors2show: if not ia: continue if isinstance(ia, vtk.vtkAssembly): #unpack assemblies acass = ia.unpack() actors2show2 += acass else: actors2show2.append(ia) # vbb, sizes, _, _ = addons.computeVisibleBounds() # kgrid = vbb[0], vbb[2], vbb[4], vbb[1], vbb[3], vbb[5] settings.notebook_plotter = k3d.plot(axes=[vp.xtitle, vp.ytitle, vp.ztitle], menu_visibility=settings.k3dMenuVisibility, height=settings.k3dPlotHeight, antialias=settings.k3dAntialias, ) # settings.notebook_plotter.grid = kgrid settings.notebook_plotter.lighting = settings.k3dLighting # set k3d camera settings.notebook_plotter.camera_auto_fit = settings.k3dCameraAutoFit settings.notebook_plotter.grid_auto_fit = settings.k3dGridAutoFit settings.notebook_plotter.axes_helper = settings.k3dAxesHelper if settings.plotter_instance and settings.plotter_instance.camera: k3dc = utils.vtkCameraToK3D(settings.plotter_instance.camera) if zoom: k3dc[0] /= zoom k3dc[1] /= zoom k3dc[2] /= zoom settings.notebook_plotter.camera = k3dc # else: # vsx, vsy, vsz = vbb[0]-vbb[1], vbb[2]-vbb[3], vbb[4]-vbb[5] # vss = numpy.linalg.norm([vsx, vsy, vsz]) # if zoom: # vss /= zoom # vfp = (vbb[0]+vbb[1])/2, (vbb[2]+vbb[3])/2, (vbb[4]+vbb[5])/2 # camera target # if viewup == 'z': # vup = (0,0,1) # camera up vector # vpos= vfp[0] + vss/1.9, vfp[1] + vss/1.9, vfp[2]+vss*0.01 # camera position # elif viewup == 'x': # vup = (1,0,0) # vpos= vfp[0]+vss*0.01, vfp[1] + vss/1.5, vfp[2] # camera position # else: # vup = (0,1,0) # vpos= vfp[0]+vss*0.01, vfp[1]+vss*0.01, vfp[2] + vss/1.5 # camera position # settings.notebook_plotter.camera = [vpos[0], vpos[1], vpos[2], # vfp[0], vfp[1], vfp[2], # vup[0], vup[1], vup[2] ] if not vp.axes: settings.notebook_plotter.grid_visible = False for ia in actors2show2: if isinstance(ia, (vtk.vtkCornerAnnotation, vtk.vtkAssembly)): continue kobj = None kcmap= None name = None if hasattr(ia, 'filename'): if ia.filename: name = os.path.basename(ia.filename) if ia.name: name = os.path.basename(ia.name) #####################################################################scalars # work out scalars first, Points Lines are also Mesh objs if isinstance(ia, (Mesh, shapes.Line, Points)): # print('scalars', ia.name, ia.N()) iap = ia.GetProperty() if isinstance(ia, (shapes.Line, Points)): iapoly = ia.polydata() else: iapoly = ia.clone().clean().triangulate().computeNormals().polydata() vtkscals = None color_attribute = None if ia.mapper().GetScalarVisibility(): vtkdata = iapoly.GetPointData() vtkscals = vtkdata.GetScalars() if vtkscals is None: vtkdata = iapoly.GetCellData() vtkscals = vtkdata.GetScalars() if vtkscals is not None: c2p = vtk.vtkCellDataToPointData() c2p.SetInputData(iapoly) c2p.Update() iapoly = c2p.GetOutput() vtkdata = iapoly.GetPointData() vtkscals = vtkdata.GetScalars() if vtkscals is not None: if not vtkscals.GetName(): vtkscals.SetName('scalars') scals_min, scals_max = ia.mapper().GetScalarRange() color_attribute = (vtkscals.GetName(), scals_min, scals_max) lut = ia.mapper().GetLookupTable() lut.Build() kcmap=[] nlut = lut.GetNumberOfTableValues() for i in range(nlut): r,g,b,a = lut.GetTableValue(i) kcmap += [i/(nlut-1), r,g,b] #####################################################################Volume if isinstance(ia, Volume): # print('Volume', ia.name, ia.dimensions()) kx, ky, kz = ia.dimensions() arr = ia.pointdata[0] kimage = arr.reshape(-1, ky, kx) colorTransferFunction = ia.GetProperty().GetRGBTransferFunction() kcmap=[] for i in range(128): r,g,b = colorTransferFunction.GetColor(i/127) kcmap += [i/127, r,g,b] kbounds = numpy.array(ia.imagedata().GetBounds()) \ + numpy.repeat(numpy.array(ia.imagedata().GetSpacing()) / 2.0, 2)\ * numpy.array([-1,1] * 3) kobj = k3d.volume(kimage.astype(numpy.float32), color_map=kcmap, #color_range=ia.imagedata().GetScalarRange(), alpha_coef=10, bounds=kbounds, name=name, ) settings.notebook_plotter += kobj #####################################################################text elif hasattr(ia, 'info') and 'formula' in ia.info.keys(): pos = (ia.GetPosition()[0],ia.GetPosition()[1]) kobj = k3d.text2d(ia.info['formula'], position=pos) settings.notebook_plotter += kobj #####################################################################Mesh elif isinstance(ia, Mesh) and ia.N() and len(ia.faces()): # print('Mesh', ia.name, ia.N(), len(ia.faces())) kobj = k3d.vtk_poly_data(iapoly, name=name, # color=_rgb2int(iap.GetColor()), color_attribute=color_attribute, color_map=kcmap, opacity=iap.GetOpacity(), wireframe=(iap.GetRepresentation()==1)) if iap.GetInterpolation() == 0: kobj.flat_shading = True settings.notebook_plotter += kobj #####################################################################Points elif isinstance(ia, Points): # print('Points', ia.name, ia.N()) kcols=[] if color_attribute is not None: scals = utils.vtk2numpy(vtkscals) kcols = k3d.helpers.map_colors(scals, kcmap, [scals_min,scals_max]).astype(numpy.uint32) # sqsize = numpy.sqrt(numpy.dot(sizes, sizes)) kobj = k3d.points(ia.points().astype(numpy.float32), color=_rgb2int(iap.GetColor()), colors=kcols, opacity=iap.GetOpacity(), shader=settings.k3dPointShader, point_size=iap.GetPointSize(), name=name, ) settings.notebook_plotter += kobj #####################################################################Lines elif ia.polydata(False).GetNumberOfLines(): # print('Line', ia.name, ia.N(), len(ia.faces()), # ia.polydata(False).GetNumberOfLines(), len(ia.lines()), # color_attribute, [vtkscals]) # kcols=[] # if color_attribute is not None: # scals = utils.vtk2numpy(vtkscals) # kcols = k3d.helpers.map_colors(scals, kcmap, # [scals_min,scals_max]).astype(numpy.uint32) # sqsize = numpy.sqrt(numpy.dot(sizes, sizes)) for i, ln_idx in enumerate(ia.lines()): if i>200: print('WARNING: K3D nr of line segments is limited to 200.') break pts = ia.points()[ln_idx] kobj = k3d.line(pts.astype(numpy.float32), color=_rgb2int(iap.GetColor()), opacity=iap.GetOpacity(), shader=settings.k3dLineShader, # width=iap.GetLineWidth()*sqsize/1000, name=name, ) settings.notebook_plotter += kobj #################################################################################### elif settings.notebookBackend == 'panel' and hasattr(vp, 'window') and vp.window: import panel # https://panel.pyviz.org/reference/panes/VTK.html vp.renderer.ResetCamera() settings.notebook_plotter = panel.pane.VTK(vp.window, width=int(vp.size[0]/1.5), height=int(vp.size[1]/2)) #################################################################################### elif 'ipyvtk' in settings.notebookBackend and hasattr(vp, 'window') and vp.window: from ipyvtklink.viewer import ViewInteractiveWidget vp.renderer.ResetCamera() settings.notebook_plotter = ViewInteractiveWidget(vp.window) #################################################################################### elif 'ipygany' in settings.notebookBackend: from ipygany import PolyMesh, Scene, IsoColor, RGB, Component from ipygany import Alpha, ColorBar, colormaps, PointCloud from ipywidgets import FloatRangeSlider, Dropdown, VBox, AppLayout, jslink bgcol = colors.rgb2hex(colors.getColor(vp.backgrcol)) actors2show2 = [] for ia in actors2show: if not ia: continue if isinstance(ia, vedo.Assembly): #unpack assemblies assacts = ia.unpack() for ja in assacts: if isinstance(ja, vedo.Assembly): actors2show2 += ja.unpack() else: actors2show2.append(ja) else: actors2show2.append(ia) pmeshes = [] colorbar = None for obj in actors2show2: # print("ipygany processing:", [obj], obj.name) if isinstance(obj, vedo.shapes.Line): lg = obj.diagonalSize()/1000 * obj.GetProperty().GetLineWidth() vmesh = vedo.shapes.Tube(obj.points(), r=lg, res=4).triangulate() vmesh.c(obj.c()) faces = vmesh.faces() # todo: Lines elif isinstance(obj, Mesh): vmesh = obj.triangulate() faces = vmesh.faces() elif isinstance(obj, Points): vmesh = obj faces = [] elif isinstance(obj, Volume): vmesh = obj.isosurface() faces = vmesh.faces() elif isinstance(obj, vedo.TetMesh): vmesh = obj.tomesh(fill=False) faces = vmesh.faces() else: print("ipygany backend: cannot process object type", [obj]) continue vertices = vmesh.points() scals = vmesh.inputdata().GetPointData().GetScalars() if scals and not colorbar: # there is an active array, only pick the first aname = scals.GetName() arr = vmesh.pointdata[aname] parr = Component(name=aname, array=arr) if len(faces): pmesh = PolyMesh(vertices=vertices, triangle_indices=faces, data={aname: [parr]}) else: pmesh = PointCloud(vertices=vertices, data={aname: [parr]}) rng = scals.GetRange() colored_pmesh = IsoColor(pmesh, input=aname, min=rng[0], max=rng[1]) if obj.scalarbar: colorbar = ColorBar(colored_pmesh) colormap_slider_range = FloatRangeSlider(value=rng, min=rng[0], max=rng[1], step=(rng[1] - rng[0]) / 100.) jslink((colored_pmesh, 'range'), (colormap_slider_range, 'value')) colormap = Dropdown( options=colormaps, description='Colormap:' ) jslink((colored_pmesh, 'colormap'), (colormap, 'index')) else: if len(faces): pmesh = PolyMesh(vertices=vertices, triangle_indices=faces) else: pmesh = PointCloud(vertices=vertices) if vmesh.alpha() < 1: colored_pmesh = Alpha(RGB(pmesh, input=tuple(vmesh.color())), input=vmesh.alpha()) else: colored_pmesh = RGB(pmesh, input=tuple(vmesh.color())) pmeshes.append(colored_pmesh) if colorbar: scene = AppLayout( left_sidebar=Scene(pmeshes, background_color=bgcol), right_sidebar=VBox((colormap_slider_range, #not working colorbar, colormap)), pane_widths=[2, 0, 1], ) else: scene = Scene(pmeshes, background_color=bgcol) settings.notebook_plotter = scene #################################################################################### elif '2d' in settings.notebookBackend.lower() and hasattr(vp, 'window') and vp.window: import PIL.Image try: import IPython except ImportError: raise Exception('IPython not available.') from vedo.io import screenshot settings.screeshotLargeImage = True nn = screenshot(returnNumpy=True, scale=settings.screeshotScale+2) pil_img = PIL.Image.fromarray(nn) settings.notebook_plotter = IPython.display.display(pil_img) return settings.notebook_plotter
def visualize_it(res_file, temp_dir=".temp", default_index=0): import pathlib import meshio from ipygany import ColorBar, IsoColor, PolyMesh, Scene, Warp, colormaps from IPython.display import clear_output, display from ipywidgets import AppLayout, Dropdown, FloatSlider, VBox, jslink from ada.core.vector_utils import vector_length res_file = pathlib.Path(res_file).resolve().absolute() suffix = res_file.suffix.lower() suffix_map = {".rmed": "med", ".vtu": None} imesh = meshio.read(res_file, file_format=suffix_map[suffix]) imesh.point_data = { key.replace(" ", "_"): value for key, value in imesh.point_data.items() } def filter_keys(var): if suffix == ".vtu" and var != "U": return False if suffix == ".rmed" and var == "point_tags": return False return True warp_data = [key for key in filter(filter_keys, imesh.point_data.keys())] magn_data = [] for d in warp_data: res = [vector_length(v[:3]) for v in imesh.point_data[d]] res_norm = [r / max(res) for r in res] magn_data_name = f"{d}_magn" imesh.point_data[magn_data_name] = np.array(res_norm, dtype=np.float64) magn_data.append(magn_data_name) imesh.field_data = { key: np.array(value) for key, value in imesh.field_data.items() } tf = (pathlib.Path(temp_dir).resolve().absolute() / res_file.name).with_suffix(".vtu") if tf.exists(): os.remove(tf) os.makedirs(tf.parent, exist_ok=True) imesh.write(tf) mesh = PolyMesh.from_vtk(str(tf)) mesh.default_color = "gray" warp_vec = warp_data[default_index] try: colored_mesh = IsoColor(mesh, input=magn_data[default_index], min=0.0, max=1.0) except KeyError as e: trace_str = traceback.format_exc() logging.error(f'KeyError "{e}"\nTrace: "{trace_str}"') colored_mesh = mesh except ImportError as e: trace_str = traceback.format_exc() logging.error("This might be") logging.error(f'ImportError "{e}"\nTrace: "{trace_str}"') return warped_mesh = Warp(colored_mesh, input=warp_vec, warp_factor=0.0) warp_slider = FloatSlider(value=0.0, min=-1.0, max=1.0) jslink((warped_mesh, "factor"), (warp_slider, "value")) # Create a colorbar widget colorbar = ColorBar(colored_mesh) # Colormap choice widget colormap = Dropdown(options=colormaps, description="colormap:") jslink((colored_mesh, "colormap"), (colormap, "index")) # EigenValue choice widget eig_map = Dropdown(options=warp_data, description="Data Value:") scene = Scene([warped_mesh]) app = AppLayout(left_sidebar=scene, right_sidebar=VBox( (eig_map, warp_slider, colormap, colorbar)), pane_widths=[2, 0, 1]) def change_input(change): vec_name = change["new"] logging.info(vec_name) colored_mesh.input = vec_name + "_magn" warped_mesh.input = vec_name # Highly inefficient but likely needed due to bug https://github.com/QuantStack/ipygany/issues/69 clear_output() display(app) eig_map.observe(change_input, names=["value"]) return app