1############################################################################### 2# Copyright (c) Lawrence Livermore National Security, LLC and other Ascent 3# Project developers. See top-level LICENSE AND COPYRIGHT files for dates and 4# other details. No copyright assignment is required to contribute to Ascent. 5############################################################################### 6 7import ipywidgets as widgets 8from traitlets import Unicode, validate, Int, List, Dict 9from IPython.display import clear_output 10 11class TrackballWidget(widgets.DOMWidget): 12 _view_name = Unicode('TrackballView').tag(sync=True) 13 _view_module = Unicode('ascent_widgets').tag(sync=True) 14 _view_module_version = Unicode('0.0.0').tag(sync=True) 15 16 width = Int(800).tag(sync=True) 17 height = Int(800).tag(sync=True) 18 image = Unicode('').tag(sync=True) 19 20 camera_info = Dict({'position': [], 'look_at': [], 'up': [], 'fov': 60}).tag(sync=True) 21 22 scene_bounds = List([]).tag(sync=True) 23 24 def __init__(self, kernelUtils, *args, **kwargs): 25 widgets.DOMWidget.__init__(self, *args, **kwargs) 26 27 self.is_connected = True 28 29 self.on_msg(self._handle_msg) 30 31 self.kernelUtils = kernelUtils 32 self.kernelUtils.set_disconnect_callback(self.disconnect) 33 34 try: 35 self._update_scene_bounds() 36 37 self._update_camera_info_from_ascent() 38 39 self._update_image() 40 except KeyError: 41 clear_output(wait=True) 42 self.close() 43 self.kernelUtils.kernel.stderr("no images found, ensure jupyter_ascent has excecuted actions and re-execute the widget") 44 45 #TODO notify the user and stop trying to handle clicks 46 def disconnect(self): 47 self.is_connected = False 48 49 def _update_image(self): 50 self.image = self.kernelUtils.get_images()[0] 51 52 def _update_camera_info(self, camera_info): 53 self.camera_info = camera_info 54 55 def _update_camera_info_from_ascent(self): 56 ascent_camera_info = self.kernelUtils.get_ascent_info()['images'][0]['camera'] 57 self._update_camera_info(ascent_camera_info) 58 59 def _update_scene_bounds(self): 60 self.scene_bounds = self.kernelUtils.get_ascent_info()['images'][0]['scene_bounds'] 61 62 def _handle_msg(self, msg, *args, **kwargs): 63 if self.is_connected: 64 content = msg["content"]["data"]["content"] 65 if content['event'] == 'keydown' or content['event'] == 'button': 66 code = content['code'] 67 if code == 87 or code == 'move_forward': #W 68 self.kernelUtils.forward() 69 elif code == 65 or code == 'move_left': #A 70 self.kernelUtils.left() 71 elif code == 83 or code == 'move_back': #S 72 self.kernelUtils.back() 73 elif code == 68 or code == 'move_right': #D 74 self.kernelUtils.right() 75 elif code == 'move_up': 76 self.kernelUtils.up() 77 elif code == 'move_down': 78 self.kernelUtils.down() 79 elif code == 'move_right': 80 self.kernelUtils.right() 81 elif code == 'move_left': 82 self.kernelUtils.left() 83 elif code == 'roll_c': 84 self.kernelUtils.roll_c() 85 elif code == 'roll_cc': 86 self.kernelUtils.roll_cc() 87 elif code == 'pitch_up': 88 self.kernelUtils.pitch_up() 89 elif code == 'pitch_down': 90 self.kernelUtils.pitch_down() 91 elif code == 'yaw_right': 92 self.kernelUtils.yaw_right() 93 elif code == 'yaw_left': 94 self.kernelUtils.yaw_left() 95 elif code == 'next': 96 #TODO this can fail 97 resp = self.kernelUtils.next_frame() 98 self.is_connected = (resp is not None) 99 100 self._update_camera_info_from_ascent() 101 102 elif content['event'] == 'mouseup': 103 camera_info = content['camera_info'] 104 self._update_camera_info(camera_info) 105 self.kernelUtils.look_at(camera_info['position'], 106 camera_info['look_at'], 107 camera_info['up']) 108 109 self._update_image() 110 else: 111 clear_output(wait=True) 112 self.close() 113 self.kernelUtils.kernel.stderr("disconnected - wait to reconnect or check the simulation hasn't ended\n") 114