After the first evaluations, my mentors and I have decided to put a hold on the 2D elements and move to creating 3D UIs and widgets.
FileMenu
Apart from continuously fixing bugs in my previous contributions, I have created a new element, which is a file select menu that allows the user to browse their file system and look at files and folders. They can navigate into and out of directories by clicking on them in the menu.
This element uses the ListBox2D I talked about in my previous post. Files are shown with Blue color and directories with Green.
The PR for this can be found here.
Orbital Menu
The first 3D element that I am creating is an orbital menu. A basic version of this was designed by my mentor 2 years ago.
The main challenge here is to ensure that the menu(disk here) around the object(cubes here) follows the camera always so that they can always be visible. For this, the vtkFollower
class is used.
My task here is to first adapt the old version of the code to work with the current codebase. Then, I have to discuss various ideas to make this menu user-friendly and sci-fi and implement them.
Experimenting with Assemblies
Assemblies are a very important part of 3D graphics as they allow us to create a hierarchical model of the elements in a scene. Imagine a 3D model of a human arm. There is a three-level hierarchy – the arm, then the hand and finally the fingers. Any transformation on a parent element in the hierarchy must be applied to the children, while allowing the children to have their own independent motion about their parent.
Thus, if a hand moves, the same motion applies to the fingers. Additionally, the fingers have their local transformations with respect to the knuckles. Assemblies help us in achieving this hierarchy.
in vtk, the vtkAssembly
class create hierarchies of elements. Parts can be added using the AddPart
member function.
I have been experimenting with this a lot to fully understand how assemblies work and wrote a small code to create a 3-level hierarchy of 3 cubes.
import dipy.viz.ui as ui import dipy.viz.window as window """ Cube actor ========== """ def cube_maker(color=None, size=(0.2, 0.2, 0.2), center=None): cube = window.vtk.vtkCubeSource() cube.SetXLength(size[0]) cube.SetYLength(size[1]) cube.SetZLength(size[2]) if center is not None: cube.SetCenter(*center) cube_mapper = window.vtk.vtkPolyDataMapper() cube_mapper.SetInputConnection(cube.GetOutputPort()) cube_actor = window.vtk.vtkActor() cube_actor.SetMapper(cube_mapper) if color is not None: cube_actor.GetProperty().SetColor(color) return cube_actor cube_actor_1 = cube_maker((1, 0, 0), (25, 25, 25), center=(0, 0, 0)) cube_actor_2 = cube_maker((0, 1, 0), (10, 10, 10), center=(50, 0, 0)) cube_actor_3 = cube_maker((0, 0, 1), (5, 5, 5), center=(100, 0, 0)) cube_actor_3.SetOrigin(cube_actor_3.GetCenter()) """ Creating assembly ================= """ assembly1 = window.vtk.vtkAssembly() assembly2 = window.vtk.vtkAssembly() assembly2.AddPart(cube_actor_2) assembly2.AddPart(cube_actor_3) assembly2.SetOrigin(cube_actor_2.GetCenter()) assembly1.AddPart(cube_actor_1) assembly1.AddPart(assembly2) """ Adding sliders to transform the cubes ===================================== """ def translate_blue_cube(slider): value = slider.value cube_actor_3.SetPosition(value, 0, 0) def translate_green_cube(slider): value = slider.value assembly2.SetPosition(value, 0, 0) def translate_red_cube(slider): value = slider.value assembly1.SetPosition(value, 0, 0) def rotate_blue_cube(slider): angle = slider.value previous_angle = slider.previous_value rotation_angle = angle - previous_angle cube_actor_3.RotateY(rotation_angle) def rotate_green_cube(slider): angle = slider.value previous_angle = slider.previous_value rotation_angle = angle - previous_angle assembly2.RotateY(rotation_angle) def rotate_red_cube(slider): angle = slider.value previous_angle = slider.previous_value rotation_angle = angle - previous_angle assembly1.RotateY(rotation_angle) ring_slider1 = ui.RingSlider2D(text_template="{angle:5.1f}°", center = (100, 500)) ring_slider1.on_change = rotate_red_cube ring_slider2 = ui.RingSlider2D(text_template="{angle:5.1f}°", center = (300, 500)) ring_slider2.on_change = rotate_green_cube ring_slider3 = ui.RingSlider2D(text_template="{angle:5.1f}°", center = (500, 500)) ring_slider3.on_change = rotate_blue_cube line_slider1 = ui.LineSlider2D(center=(100, 100), initial_value=0, min_value=-10, max_value=10, length=150) line_slider1.on_change = translate_red_cube line_slider2 = ui.LineSlider2D(center=(300, 100), initial_value=0, min_value=-10, max_value=10, length=150) line_slider2.on_change = translate_green_cube line_slider3 = ui.LineSlider2D(center=(500, 100), initial_value=0, min_value=-10, max_value=10, length=150) line_slider3.on_change = translate_blue_cube """ Adding Elements to the ShowManager ================================== Once all elements have been initialised, they have to be added to the show manager in the following manner. """ current_size = (600, 600) show_manager = window.ShowManager(size=current_size) show_manager.ren.add(assembly1) show_manager.ren.add(ring_slider1) show_manager.ren.add(ring_slider2) show_manager.ren.add(ring_slider3) show_manager.ren.add(line_slider1) show_manager.ren.add(line_slider2) show_manager.ren.add(line_slider3) show_manager.start()
I use the “Peek” screen recorder on Ubuntu to record these demos.