├── .gitignore ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── VERSION ├── __init__.py ├── controller ├── __init__.py ├── controller.py ├── controller_detector.py ├── controller_filter.py ├── controller_options.py ├── controller_scene.py └── controller_stream.py ├── core ├── __init__.py ├── color.py ├── hdr_graphics_view_base.py ├── hdr_image.py ├── highlighter_base.py ├── logger.py ├── messages.py ├── plot_2d.py ├── plot_2d_xy.py ├── plot_figure_base.py ├── plot_rgb.py ├── plugin.py ├── point.py ├── pyside2_uic.py └── vector.py ├── detector ├── __init__.py └── detector.py ├── emca.py ├── filter ├── __init__.py ├── filter.py └── filter_settings.py ├── model ├── __init__.py ├── camera_data.py ├── contribution_data.py ├── intersection_data.py ├── mesh_data.py ├── model.py ├── options_data.py ├── path_data.py ├── pixel_data.py ├── render_info.py └── user_data.py ├── plugins ├── __init__.py ├── plugin_intersection_data │ ├── __init__.py │ ├── intersection_data_plot_2d.py │ ├── intersection_data_plot_3d.py │ ├── intersection_data_plot_rgb.py │ ├── plot_list_item.py │ ├── plugin_intersection_data_plots.py │ └── ui │ │ └── plugin_plots.ui ├── plugin_spherical_view │ ├── __init__.py │ ├── plugin_spherical_view.py │ ├── spherical_graphics_view.py │ ├── ui │ │ ├── spherical_view.ui │ │ └── spherical_view_image.ui │ └── view_spherical_view_image.py ├── plugins_handler.py └── plugins_view_container.py ├── renderer ├── __init__.py ├── camera.py ├── mesh.py ├── path.py ├── renderer.py ├── rubberband.py ├── scene_renderer.py ├── shape.py └── sphere.py ├── requirements.txt ├── resources ├── breeze_resources.py ├── dark.qss ├── dark │ ├── branch_closed-on.svg │ ├── branch_closed.svg │ ├── branch_open-on.svg │ ├── branch_open.svg │ ├── checkbox_checked.svg │ ├── checkbox_checked_disabled.svg │ ├── checkbox_indeterminate.svg │ ├── checkbox_indeterminate_disabled.svg │ ├── checkbox_unchecked.svg │ ├── checkbox_unchecked_disabled.svg │ ├── close-hover.svg │ ├── close-pressed.svg │ ├── close.svg │ ├── down_arrow-hover.svg │ ├── down_arrow.svg │ ├── down_arrow_disabled.svg │ ├── hmovetoolbar.svg │ ├── hsepartoolbar.svg │ ├── left_arrow.svg │ ├── left_arrow_disabled.svg │ ├── radio_checked.svg │ ├── radio_checked_disabled.svg │ ├── radio_unchecked.svg │ ├── radio_unchecked_disabled.svg │ ├── right_arrow.svg │ ├── right_arrow_disabled.svg │ ├── sizegrip.svg │ ├── spinup_disabled.svg │ ├── stylesheet-branch-end-closed.svg │ ├── stylesheet-branch-end-open.svg │ ├── stylesheet-branch-end.svg │ ├── stylesheet-branch-more.svg │ ├── stylesheet-vline.svg │ ├── transparent.svg │ ├── undock-hover.svg │ ├── undock.svg │ ├── up_arrow-hover.svg │ ├── up_arrow.svg │ ├── up_arrow_disabled.svg │ ├── vmovetoolbar.svg │ └── vsepartoolbars.svg ├── emca.png ├── light.qss ├── light │ ├── branch_closed-on.svg │ ├── branch_closed.svg │ ├── branch_open-on.svg │ ├── branch_open.svg │ ├── checkbox_checked-hover.svg │ ├── checkbox_checked.svg │ ├── checkbox_checked_disabled.svg │ ├── checkbox_indeterminate-hover.svg │ ├── checkbox_indeterminate.svg │ ├── checkbox_indeterminate_disabled.svg │ ├── checkbox_unchecked-hover.svg │ ├── checkbox_unchecked_disabled.svg │ ├── close-hover.svg │ ├── close-pressed.svg │ ├── close.svg │ ├── down_arrow-hover.svg │ ├── down_arrow.svg │ ├── down_arrow_disabled.svg │ ├── hmovetoolbar.svg │ ├── hsepartoolbar.svg │ ├── left_arrow.svg │ ├── left_arrow_disabled.svg │ ├── radio_checked-hover.svg │ ├── radio_checked.svg │ ├── radio_checked_disabled.svg │ ├── radio_unchecked-hover.svg │ ├── radio_unchecked_disabled.svg │ ├── right_arrow.svg │ ├── right_arrow_disabled.svg │ ├── sizegrip.svg │ ├── spinup_disabled.svg │ ├── stylesheet-branch-end-closed.svg │ ├── stylesheet-branch-end-open.svg │ ├── stylesheet-branch-end.svg │ ├── stylesheet-branch-more.svg │ ├── stylesheet-vline.svg │ ├── transparent.svg │ ├── undock-hover.svg │ ├── undock.svg │ ├── up_arrow-hover.svg │ ├── up_arrow.svg │ ├── up_arrow_disabled.svg │ ├── vmovetoolbar.svg │ └── vsepartoolbars.svg └── options.ini ├── server ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include │ └── emca │ │ ├── dataapi.h │ │ ├── datatypes.h │ │ ├── emcaserver.h │ │ ├── heatmapdata.h │ │ ├── messages.h │ │ ├── pathdata.h │ │ ├── platform.h │ │ ├── plugin.h │ │ ├── renderinterface.h │ │ ├── scenedata.h │ │ └── stream.h └── src │ ├── dataapi.cpp │ ├── emcaserver.cpp │ ├── heatmapdata.cpp │ └── pathdata.cpp ├── stream ├── __init__.py ├── socket_stream.py ├── socket_stream_client.py └── stream.py └── view ├── __init__.py ├── ui ├── about.ui ├── connect.ui ├── detector.ui ├── emca.ui ├── filter.ui ├── options.ui ├── pixel_data.ui ├── render_image.ui ├── render_info.ui ├── render_scene.ui └── render_scene_options.ui ├── view_main ├── __init__.py ├── main_view.py ├── pixel_icon.py ├── popup_messages.py ├── view_about.py ├── view_connect_settings.py ├── view_detector_settings.py ├── view_emca.py ├── view_filter_settings.py ├── view_options_settings.py └── view_render_settings.py ├── view_pixel_data ├── __init__.py └── view_pixel_data.py ├── view_render_image ├── __init__.py ├── hdr_graphics_view.py └── view_render_image.py ├── view_render_scene ├── __init__.py ├── view_render_scene.py └── view_render_scene_options.py └── view_sample_contribution ├── __init__.py ├── view_sample_contribution_plot.py ├── view_sample_depth_plot.py └── view_sample_lum_plot.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | /resources/options.ini 4 | emca_debug.log 5 | /server/build 6 | CMakeLists.txt.user 7 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Christoph Kreisl 2 | Lukas Ruppert 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Christoph Kreisl 4 | Copyright (c) 2021 Lukas Ruppert 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.0.0 -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/__init__.py -------------------------------------------------------------------------------- /controller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/controller/__init__.py -------------------------------------------------------------------------------- /controller/controller_detector.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.messages import StateMsg 27 | import logging 28 | import typing 29 | 30 | from model.model import Model 31 | from view.view_main.main_view import MainView 32 | 33 | from typing import TYPE_CHECKING 34 | if TYPE_CHECKING: 35 | from controller.controller import Controller 36 | else: 37 | from typing import Any as Controller 38 | 39 | class ControllerDetector(object): 40 | 41 | """ 42 | ControllerDetector 43 | Handles the core logic of the detector which is used to determine high variance sample contributions. 44 | Two methods are currently used. 45 | 1.) based on standard deviation 46 | 2.) ESD by Rosner 47 | """ 48 | 49 | def __init__(self, parent : Controller, model : Model, view : MainView): 50 | self._controller_main = parent 51 | self._model = model 52 | self._view = view 53 | # init detector view with values from detector class 54 | self._view.view_detector.init_values(model.detector) 55 | 56 | def handle_state_msg(self, tpl : typing.Tuple[StateMsg, typing.Any]): 57 | """ 58 | Handle current state, messages mostly received from thread, 59 | which listens on the socket pipeline for incoming messages 60 | """ 61 | msg = tpl[0] 62 | if msg is StateMsg.DATA_PIXEL: 63 | # check if detector is enabled and run outlier detection 64 | if self._model.detector.is_active: 65 | self.run_detector() 66 | 67 | def update_and_run_detector(self, m, alpha, k, pre_filter, is_default, is_active): 68 | """ 69 | Saves all user changes of the detector 70 | :param m: 71 | :param alpha: 72 | :param k: 73 | :param pre_filter: 74 | :param is_default: 75 | :param is_active: 76 | :return: 77 | """ 78 | self._model.detector.update_values(m, alpha, k, pre_filter, is_default, is_active) 79 | # run detector if sample contribution data is available 80 | if self._model.final_estimate_data.data_loaded: 81 | self.run_detector() 82 | else: 83 | self._view.view_popup.error_no_final_estimate_data("") 84 | 85 | def run_detector(self): 86 | """ 87 | Only runs the detector if the checkbox for the detector is active, 88 | moreover the final estimate data is needed in order to detector outliers 89 | :return: 90 | """ 91 | detector = self._model.detector 92 | if detector.is_active: 93 | data = self._model.final_estimate_data.mean 94 | path_outliers_indices = detector.run_outlier_detection(data=data) 95 | if len(path_outliers_indices) > 0: 96 | self._controller_main.update_path(path_outliers_indices, False) 97 | else: 98 | self._view.view_popup.error_outlier_detector_no_outliers_detected("") 99 | else: 100 | self._view.view_popup.error_detector_not_enabled("") 101 | 102 | -------------------------------------------------------------------------------- /controller/controller_scene.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | import typing 27 | from PySide2.QtCore import Slot 28 | from core.messages import StateMsg 29 | from model.model import Model 30 | from view.view_main.main_view import MainView 31 | 32 | from typing import TYPE_CHECKING 33 | if TYPE_CHECKING: 34 | from controller.controller import Controller 35 | else: 36 | from typing import Any as Controller 37 | 38 | class ControllerRenderScene(object): 39 | 40 | """ 41 | ControllerRenderScene 42 | Handles the interaction and logic with the RenderScene and RenderSceneOptions View. 43 | """ 44 | 45 | def __init__(self, parent : Controller, model : Model, view : MainView): 46 | self._controller_main = parent 47 | self._model = model 48 | self._view = view 49 | 50 | def handle_state_msg(self, tpl : typing.Tuple[StateMsg, typing.Any]): 51 | """ 52 | Handle current state, messages mostly received from thread, 53 | which listens on the socket pipeline for incoming messages 54 | """ 55 | msg = tpl[0] 56 | if msg is StateMsg.DATA_INFO: 57 | self._controller_main.stream.request_camera_data() 58 | # automatically request scene data once render info is available 59 | if self._model.options_data.auto_scene_load: 60 | self._controller_main.stream.request_scene_data() 61 | elif msg is StateMsg.DATA_CAMERA: 62 | self._view.view_render_scene.scene_renderer.load_camera(tpl[1]) 63 | elif msg is StateMsg.DATA_MESH: 64 | self._view.view_render_scene.scene_renderer.load_mesh(tpl[1]) 65 | elif msg is StateMsg.DATA_SCENE_INFO: 66 | self._view.view_render_scene.scene_renderer.process_scene_info(tpl[1]) 67 | 68 | @Slot(bool) 69 | def reset_camera_position(self, clicked : bool): 70 | """ 71 | Reset the camera position to its default origin position 72 | """ 73 | self._view.view_render_scene.scene_renderer.reset_camera_position() 74 | 75 | def reset_path_options(self): 76 | self._view.view_render_scene.scene_renderer.reset_path_options() 77 | 78 | def reset_scene_options(self): 79 | self._view.view_render_scene.scene_renderer.reset_scene_options() 80 | 81 | def reset_heatmap_options(self): 82 | self._view.view_render_scene.scene_renderer.reset_heatmap_options() 83 | 84 | def update_path_options(self, path_options : typing.Dict[str, typing.Any]): 85 | self._view.view_render_scene.scene_renderer.path_options = path_options 86 | self._view.view_render_scene_options.load_path_options(path_options) 87 | 88 | def update_scene_options(self, scene_options : typing.Dict[str, typing.Any]): 89 | self._view.view_render_scene.scene_renderer.scene_options = scene_options 90 | self._view.view_render_scene_options.load_scene_options(scene_options) 91 | 92 | def update_heatmap_options(self, heatmap_options : typing.Dict[str, typing.Any]): 93 | self._view.view_render_scene.scene_renderer.heatmap_options = heatmap_options 94 | self._view.view_render_scene_options.load_heatmap_options(heatmap_options) 95 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/core/__init__.py -------------------------------------------------------------------------------- /core/highlighter_base.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from matplotlib.figure import Figure 27 | from matplotlib.axes import Axes 28 | from matplotlib.widgets import RectangleSelector 29 | import numpy as np 30 | import logging 31 | 32 | 33 | rectprops = dict( 34 | facecolor='white', 35 | edgecolor='white', 36 | alpha=0.2, 37 | fill=True) 38 | 39 | 40 | class HighlighterBase(object): 41 | 42 | def __init__(self, figure : Figure, axes : Axes, callback): 43 | self.fig = figure 44 | self.axes = axes 45 | self.canvas = self.fig.canvas 46 | 47 | self._cid_toggle = self.fig.canvas.mpl_connect('key_press_event', self.key_press_event) 48 | self._cid_release = self.fig.canvas.mpl_connect('key_release_event', self.key_release_event) 49 | self._cid_pick = self.fig.canvas.mpl_connect('pick_event', self.pick_event) 50 | 51 | self._rs = [] 52 | self._active = False 53 | self._hold_shift = False 54 | self.callback_send_update_path = callback 55 | 56 | def add_rectangle_selector(self, axes : Axes, select_func): 57 | rs = RectangleSelector(axes, select_func, useblit=True, rectprops=rectprops) 58 | self._rs.append(rs) 59 | 60 | def clear_rectangle_selectors(self): 61 | self._rs.clear() 62 | 63 | def delete_highlighter(self): 64 | del self.highlighters 65 | self.highlighters = None 66 | 67 | def pick_event(self, event): 68 | if self.callback_send_update_path is None: 69 | return 70 | ind = event.ind 71 | if self._hold_shift: 72 | self.callback_send_update_path(np.array([ind[0]]), True) 73 | else: 74 | self.callback_send_update_path(np.array([ind[0]]), False) 75 | 76 | def key_press_event(self, event): 77 | if event.key in ['R', 'r']: 78 | logging.info("Pressed key R") 79 | self._active = not self._active 80 | self.enable_rectangle_selector(self._active) 81 | self.enable_pick_event_selector(not self._active) 82 | if event.key == 'shift': 83 | logging.info("Shift press") 84 | self._hold_shift = True 85 | 86 | def key_release_event(self, event): 87 | if event.key == 'shift': 88 | logging.info("Shift release") 89 | self._hold_shift = False 90 | 91 | def enable_rectangle_selector(self, enable : bool): 92 | for rs in self._rs: 93 | rs.set_active(enable) 94 | 95 | def enable_pick_event_selector(self, enable : bool): 96 | if enable: 97 | logging.info("connect pick event") 98 | self._cid_pick = self.fig.canvas.mpl_connect('pick_event', self.pick_event) 99 | else: 100 | logging.info("disconnect pick event") 101 | self.fig.canvas.mpl_disconnect(self._cid_pick) 102 | 103 | def enable_multi_selection(self, enabled : bool): 104 | if enabled: 105 | self._cid_toggle = self.fig.canvas.mpl_connect('key_press_event', self.key_press_event) 106 | self._cid_release = self.fig.canvas.mpl_connect('key_release_event', self.key_release_event) 107 | else: 108 | self.fig.canvas.mpl_disconnect(self._cid_toggle) 109 | self.fig.canvas.mpl_disconnect(self._cid_release) 110 | 111 | def overwrite_pick_event(self, func_callback): 112 | self._cid_pick = self.fig.canvas.mpl_connect('pick_event', func_callback) 113 | 114 | def inside(self, event1, event2, x, y): 115 | x0, x1 = sorted([event1.xdata, event2.xdata]) 116 | y0, y1 = sorted([event1.ydata, event2.ydata]) 117 | return (x > x0) & (x < x1) & (y > y0) & (y < y1) 118 | -------------------------------------------------------------------------------- /core/logger.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | import logging 27 | 28 | 29 | FORMATTER = '%(asctime)s - %(name)s - %(levelname)s - [%(module)s] - %(message)s' 30 | LOGNAME = 'emca_debug.log' 31 | 32 | 33 | def InitLogSystem(): 34 | """ 35 | Init the log system 36 | :return: 37 | """ 38 | # set up logger system 39 | logger = logging.getLogger() 40 | logger.setLevel(logging.INFO) 41 | fh = logging.FileHandler(LOGNAME, mode='w') 42 | fh.setLevel(logging.INFO) 43 | ch = logging.StreamHandler() 44 | ch.setLevel(logging.INFO) 45 | formatter = logging.Formatter(FORMATTER) 46 | fh.setFormatter(formatter) 47 | ch.setFormatter(formatter) 48 | logger.addHandler(fh) 49 | logger.addHandler(ch) 50 | -------------------------------------------------------------------------------- /core/messages.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from enum import Enum 27 | 28 | class StateMsg(Enum): 29 | DISCONNECT = 0 30 | CONNECT = 1 31 | SERVER_ERROR = 2 32 | DATA_IMAGE = 3 33 | DATA_PIXEL = 4 34 | DATA_CAMERA = 5 35 | DATA_SCENE_INFO = 6 36 | DATA_MESH = 7 37 | DATA_INFO = 8 38 | DATA_NOT_VALID = 9 39 | DATA_3D_PATHS = 10 40 | DATA_DETECTOR = 11 41 | UPDATE_PLUGIN = 12 42 | SUPPORTED_PLUGINS = 13 43 | QUIT = 14 44 | 45 | 46 | class ServerMsg(Enum): 47 | # connection management (0x000x) 48 | EMCA_HELLO = 0x0001 49 | EMCA_SUPPORTED_PLUGINS = 0x0002 50 | EMCA_DISCONNECT = 0x000E 51 | EMCA_QUIT = 0x000F 52 | 53 | # requests from the client (0x001x) 54 | EMCA_REQUEST_RENDER_INFO = 0x0011 55 | EMCA_REQUEST_RENDER_IMAGE = 0x0012 56 | EMCA_REQUEST_RENDER_PIXEL = 0x0013 57 | EMCA_REQUEST_CAMERA = 0x0014 58 | EMCA_REQUEST_SCENE = 0x0015 59 | 60 | # responses to the client (0x002x) 61 | EMCA_RESPONSE_RENDER_INFO = 0x0021 62 | EMCA_RESPONSE_RENDER_IMAGE = 0x0022 63 | EMCA_RESPONSE_RENDER_PIXEL = 0x0023 64 | EMCA_RESPONSE_CAMERA = 0x0024 65 | EMCA_RESPONSE_SCENE = 0x0025 66 | 67 | @staticmethod 68 | def get_server_msg(flag): 69 | return { 70 | # connection management (0x000x) 71 | 0x0001: ServerMsg.EMCA_HELLO, 72 | 0x0002: ServerMsg.EMCA_SUPPORTED_PLUGINS, 73 | 0x000E: ServerMsg.EMCA_DISCONNECT, 74 | 0x000F: ServerMsg.EMCA_QUIT, 75 | 76 | # requests from the client (0x001x) 77 | 0x0011: ServerMsg.EMCA_REQUEST_RENDER_INFO, 78 | 0x0012: ServerMsg.EMCA_REQUEST_RENDER_IMAGE, 79 | 0x0013: ServerMsg.EMCA_REQUEST_RENDER_PIXEL, 80 | 0x0014: ServerMsg.EMCA_REQUEST_CAMERA, 81 | 0x0015: ServerMsg.EMCA_REQUEST_SCENE, 82 | 83 | # responses to the client (0x002x) 84 | 0x0021: ServerMsg.EMCA_RESPONSE_RENDER_INFO, 85 | 0x0022: ServerMsg.EMCA_RESPONSE_RENDER_IMAGE, 86 | 0x0023: ServerMsg.EMCA_RESPONSE_RENDER_PIXEL, 87 | 0x0024: ServerMsg.EMCA_RESPONSE_CAMERA, 88 | 0x0025: ServerMsg.EMCA_RESPONSE_SCENE 89 | }.get(flag, None) 90 | 91 | 92 | class ShapeType(Enum): 93 | TriangleMesh = 0 94 | SphereMesh = 1 95 | -------------------------------------------------------------------------------- /core/plot_2d_xy.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | import matplotlib.pyplot as plt 27 | import numpy as np 28 | from core.plot_figure_base import FigureBase 29 | from core.highlighter_base import HighlighterBase 30 | 31 | 32 | class ScatterPlot2DXY(FigureBase): 33 | 34 | def __init__(self, callback=None, title=None): 35 | figure, axes = plt.subplots(figsize=(5, 5), nrows=2, ncols=2, 36 | gridspec_kw={"width_ratios":(9, 1), "height_ratios":(1, 9), "hspace":0.05, "wspace":0.05, "left":0.1, "right":0.95, "top":0.92, "bottom":0.08}, 37 | constrained_layout=True) 38 | FigureBase.__init__(self, figure, axes) 39 | self.highlighter = HighlighterBase(figure, axes, callback) 40 | 41 | self.line = None 42 | self.x = None 43 | self.hist_x = None 44 | self.hist_y = None 45 | 46 | for ax in self.axes.flatten(): 47 | ax.spines['top'].set_visible(False) 48 | ax.spines['right'].set_visible(False) 49 | 50 | self.axes[0, 0].set_axis_off() 51 | self.axes[0, 0].add_artist(self.axes[0, 0].patch) 52 | self.axes[0, 0].patch.set_zorder(-1) 53 | 54 | self.axes[0, 1].set_axis_off() 55 | 56 | self.axes[1, 1].set_axis_off() 57 | self.axes[1, 1].add_artist(self.axes[1, 1].patch) 58 | self.axes[1, 1].patch.set_zorder(-1) 59 | 60 | self.axes[1, 0].xaxis.set_ticks_position('bottom') 61 | self.axes[1, 0].yaxis.set_ticks_position('left') 62 | 63 | self.axes[0, 0].set_title(title, color=self.color_title) 64 | 65 | self.plot_2d([], [], []) 66 | 67 | self.highlighter.add_rectangle_selector(self.axes[1, 0], self.select) 68 | self.highlighter.enable_rectangle_selector(False) 69 | 70 | def clear(self): 71 | if self.line is not None: 72 | self.line.remove() 73 | self.line = None 74 | if self.hist_x is not None and self.hist_y is not None: 75 | self.hist_x.remove() 76 | self.hist_y.remove() 77 | self.hist_x = None 78 | self.hist_y = None 79 | 80 | for ax in self.figure.axes: 81 | ax.relim() 82 | 83 | def plot_2d(self, x, y, z): 84 | self.clear() 85 | 86 | self.x = x 87 | self.line = self.axes[1, 0].scatter(y, z, color=self.color_dots, picker=True, pickradius=5, alpha=self.alpha_dots) 88 | _, _, self.hist_y = self.axes[0, 0].hist(y, bins=10, color=self.color_dots, orientation='vertical') 89 | _, _, self.hist_x = self.axes[1, 1].hist(z, bins=10, color=self.color_dots, orientation='horizontal') 90 | 91 | self.redraw() 92 | 93 | def set_title(self, title): 94 | self.axes[0, 0].set_title(title, color=self.color_title) 95 | 96 | self.redraw() 97 | 98 | # point selection 99 | def select(self, mousedown, mouseup): 100 | mask = self.highlighter.inside(mousedown, mouseup, self.line.get_offsets()[:,0], self.line.get_offsets()[:,1]) 101 | self.highlighter.callback_send_update_path(np.int32(self.line.get_offsets()[mask,0]), self.highlighter._hold_shift) 102 | 103 | # point highlighting 104 | def highlight(self, path_indices : np.ndarray): 105 | """ 106 | Highlight the given indices 107 | """ 108 | mask = np.isin(self.x, path_indices) 109 | self.line.set_color(np.where(mask, 'yellow', self.color_dots)) 110 | 111 | self.redraw() 112 | 113 | -------------------------------------------------------------------------------- /core/plot_figure_base.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | import matplotlib.pyplot as plt 27 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas 28 | from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolBar 29 | from PySide2.QtWidgets import QSizePolicy 30 | from PySide2.QtCore import Qt 31 | import numpy as np 32 | import logging 33 | 34 | plt.rcParams.update({ 35 | 'ytick.color': 'white', 36 | 'xtick.color': 'white', 37 | 'axes.labelcolor': 'white', 38 | 'axes.edgecolor': 'white' 39 | }) 40 | 41 | 42 | class FigureBase(FigureCanvas): 43 | 44 | def __init__(self, figure, axes): 45 | FigureCanvas.__init__(self, figure) 46 | FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) 47 | FigureCanvas.updateGeometry(self) 48 | self.RGBA = '#31363b' 49 | self.plot_facecolor = '#232629' 50 | self.figure.patch.set_facecolor(self.RGBA) 51 | self.axes = axes 52 | self.setFocusPolicy(Qt.ClickFocus) 53 | self.setFocus() 54 | 55 | if isinstance(self.axes, np.ndarray): 56 | for ax in self.figure.axes: 57 | ax.set_facecolor(self.plot_facecolor) 58 | else: 59 | self.axes.set_facecolor(self.plot_facecolor) 60 | 61 | self.alpha_dots = 0.7 62 | self.color_dots = '#eff0f1' 63 | self.color_title = 'white' 64 | self.color_axes = 'white' 65 | 66 | def create_navigation_toolbar(self, parent=None): 67 | return NavigationToolBar(self, parent) 68 | 69 | def apply_theme(self, theme : str): 70 | if theme == 'light': 71 | self.RGBA = '#EFF0F1' 72 | self.plot_facecolor = '#EFF0F1' 73 | self.color_axes = 'black' 74 | self.color_title = 'black' 75 | self.color_dots = 'blue' 76 | else: 77 | self.RGBA = '#31363b' 78 | self.plot_facecolor = '#232629' 79 | self.color_axes = 'white' 80 | self.color_title = 'white' 81 | self.color_dots = '#eff0f1' 82 | self.figure.patch.set_facecolor(self.RGBA) 83 | if isinstance(self.axes, np.ndarray): 84 | for ax in self.figure.axes: 85 | ax.set_facecolor(self.plot_facecolor) 86 | ax.tick_params(axis='x', colors=self.color_axes) 87 | ax.tick_params(axis='y', colors=self.color_axes) 88 | for spine in ['left', 'right', 'bottom', 'top']: 89 | ax.spines[spine].set_color(self.color_axes) 90 | else: 91 | self.axes.set_facecolor(self.plot_facecolor) 92 | self.axes.tick_params(axis='x', colors=self.color_axes) 93 | self.axes.tick_params(axis='y', colors=self.color_axes) 94 | for spine in ['left', 'right', 'bottom', 'top']: 95 | self.axes.spines[spine].set_color(self.color_axes) 96 | self.redraw() 97 | 98 | def clear(self): 99 | if isinstance(self.axes, np.ndarray): 100 | for ax in self.figure.axes: 101 | ax.clear() 102 | else: 103 | self.axes.clear() 104 | self.redraw() 105 | 106 | def redraw(self): 107 | #logging.info("redraw requested") 108 | self.figure.canvas.draw_idle() 109 | -------------------------------------------------------------------------------- /detector/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/detector/__init__.py -------------------------------------------------------------------------------- /emca.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from PySide2.QtWidgets import QApplication 27 | from PySide2.QtCore import QFile, QTextStream, QCoreApplication 28 | from PySide2.QtCore import Qt 29 | from core.logger import InitLogSystem 30 | import sys 31 | import os 32 | 33 | from model.model import Model 34 | from view.view_main.main_view import MainView 35 | from controller.controller import Controller 36 | from renderer.scene_renderer import SceneRenderer 37 | import resources.breeze_resources 38 | 39 | 40 | class EMCAClient(object): 41 | 42 | """ 43 | Explorer of Monte Carlo based Algorithms - Client 44 | """ 45 | 46 | def __init__(self): 47 | self.controller = Controller(Model(), MainView()) 48 | self.controller.init_scene_renderer(SceneRenderer()) 49 | 50 | def load_theme(self, qt_app): 51 | theme = self.controller.options.get_theme() 52 | theme_files = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', '{}.qss'.format(theme))) 53 | file = QFile(theme_files) 54 | file.open(QFile.ReadOnly | QFile.Text) 55 | text_stream = QTextStream(file) 56 | qt_app.setStyleSheet(text_stream.readAll()) 57 | self.controller.options.set_theme(theme) 58 | 59 | def start(self): 60 | """ 61 | Starts and opens the GUI of EMCA 62 | :return: 63 | """ 64 | self.controller.display_view() 65 | 66 | 67 | if __name__ == '__main__': 68 | if sys.version_info < (3, 7): 69 | print("Using Python 3.7 or newer is highly recommended.") 70 | 71 | QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts) 72 | InitLogSystem() 73 | app = QApplication(sys.argv) 74 | emca_client = EMCAClient() 75 | emca_client.load_theme(app) 76 | emca_client.start() 77 | sys.exit(app.exec_()) 78 | -------------------------------------------------------------------------------- /filter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/filter/__init__.py -------------------------------------------------------------------------------- /model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/model/__init__.py -------------------------------------------------------------------------------- /model/camera_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.vector import Vec3f 27 | from core.point import Point3f 28 | from stream.stream import Stream 29 | # import logging 30 | 31 | 32 | class CameraData(object): 33 | 34 | """ 35 | Camera Data 36 | Holds information about the camera data 37 | Will be used by the renderer interface to initialise the camera for the 3D scene viewer 38 | """ 39 | 40 | def __init__(self): 41 | self._position = Point3f(0, 0, 0) 42 | self._direction = Vec3f(1, 0, 0) 43 | self._up = Vec3f(0, 0, 1) 44 | self._near_clip = 5.0 45 | self._far_clip = 15.0 46 | self._fov = 45.0 47 | 48 | def deserialize(self, stream : Stream): 49 | """ 50 | Deserializes all camera information from the socket stream 51 | :param stream: SocketStream 52 | :return: 53 | """ 54 | self._position = stream.read_point3f() 55 | self._direction = stream.read_vec3f() 56 | self._up = stream.read_vec3f() 57 | self._near_clip = stream.read_float() 58 | self._far_clip = stream.read_float() 59 | self._fov = stream.read_float() 60 | 61 | # logging.info(self.to_string()) 62 | 63 | @property 64 | def position(self) -> Point3f: 65 | """ 66 | Returns the camera's 3D position in world space 67 | :return: 68 | """ 69 | return self._position 70 | 71 | @property 72 | def direction(self) -> Vec3f: 73 | """ 74 | Returns the camera viewing direction 75 | :return: 76 | """ 77 | return self._direction 78 | 79 | @property 80 | def up(self) -> Vec3f: 81 | """ 82 | Returns the camera up vector 83 | :return: 84 | """ 85 | return self._up 86 | 87 | @property 88 | def near_clip(self) -> float: 89 | """ 90 | Returns the camera near clip value 91 | :return: 92 | """ 93 | return self._near_clip 94 | 95 | @property 96 | def far_clip(self) -> float: 97 | """ 98 | Returns the camera far clip value 99 | :return: 100 | """ 101 | return self._far_clip 102 | 103 | @property 104 | def fov(self) -> float: 105 | """ 106 | Returns the camera the field of view angle 107 | :return: 108 | """ 109 | return self._fov 110 | 111 | def to_string(self) -> str: 112 | """ 113 | Returns all camera information within a string 114 | :return: 115 | """ 116 | return 'position = {} \n' \ 117 | 'direction = {} \n' \ 118 | 'up = {} \n' \ 119 | 'neaClip = {} \n' \ 120 | 'farClip = {} \n' \ 121 | 'fov = {} \n'.format(self._position.to_string(), 122 | self._direction.to_string(), 123 | self._up.to_string(), 124 | self._near_clip, 125 | self._far_clip, 126 | self._fov) 127 | 128 | 129 | -------------------------------------------------------------------------------- /model/intersection_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.point import Point3f 27 | from core.color import Color4f 28 | from stream.stream import Stream 29 | from model.user_data import UserData 30 | 31 | import typing 32 | 33 | 34 | class IntersectionData(UserData): 35 | 36 | """ 37 | IntersectionData 38 | Represents one intersection point of a traced path through the scene. 39 | Holds information about the intersection, more precisely the intersection position, the intersection index, 40 | if a next event estimation was set, if a intersection position was set and current estimate information at this point. 41 | """ 42 | 43 | def __init__(self, stream : Stream): 44 | super().__init__(stream) 45 | 46 | self._depth_idx = stream.read_uint() 47 | 48 | # position 49 | self._pos = None 50 | if stream.read_bool(): 51 | self._pos = stream.read_point3f() 52 | 53 | # next event estimation position 54 | self._pos_ne = None 55 | self._visible_ne = None 56 | if stream.read_bool(): 57 | self._pos_ne = stream.read_point3f() 58 | self._visible_ne = stream.read_bool() 59 | 60 | # the incident radiance estimate of the entire path evaluated up to this point 61 | self._li = None 62 | if stream.read_bool(): 63 | self._li = stream.read_color4f() 64 | 65 | # emission 66 | self._le = None 67 | if stream.read_bool(): 68 | self._le = stream.read_color4f() 69 | 70 | @property 71 | def depth_idx(self) -> typing.Optional[int]: 72 | """ 73 | Returns the current depth index (intersection index) 74 | """ 75 | return self._depth_idx 76 | 77 | @property 78 | def is_ne_visible(self) -> typing.Optional[bool]: 79 | """ 80 | Returns if the next estimation is occluded 81 | """ 82 | return self._visible_ne 83 | 84 | @property 85 | def pos(self) -> typing.Optional[Point3f]: 86 | """ 87 | Returns the intersection position 88 | """ 89 | return self._pos 90 | 91 | @property 92 | def pos_ne(self) -> typing.Optional[Point3f]: 93 | """ 94 | Returns the position of the next event estimation 95 | """ 96 | return self._pos_ne 97 | 98 | @property 99 | def li(self) -> typing.Optional[Color4f]: 100 | """ 101 | Returns the current estimate at this intersection / path position 102 | """ 103 | return self._li 104 | 105 | @property 106 | def le(self) -> typing.Optional[Color4f]: 107 | """ 108 | Returns the emission at this intersection / path position 109 | """ 110 | return self._le 111 | 112 | -------------------------------------------------------------------------------- /model/path_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.color import Color4f 27 | from core.point import Point3f 28 | import typing 29 | from stream.stream import Stream 30 | from model.intersection_data import IntersectionData 31 | from model.user_data import UserData 32 | 33 | class PathData(UserData): 34 | 35 | """ 36 | PathData 37 | Represents one traced path with added user data 38 | """ 39 | 40 | def __init__(self, stream : Stream): 41 | super().__init__(stream) 42 | self._sample_idx = stream.read_uint() 43 | self._path_depth = stream.read_uint() 44 | self._path_origin = stream.read_point3f() 45 | 46 | self._final_estimate = None 47 | if stream.read_bool(): 48 | self._final_estimate = stream.read_color4f() 49 | 50 | self._dict_intersections = {} 51 | self._intersection_count = stream.read_uint() 52 | for i in range(0, self._intersection_count): 53 | intersection = IntersectionData(stream) 54 | self._dict_intersections[intersection.depth_idx] = intersection 55 | 56 | @property 57 | def final_estimate(self) -> Color4f: 58 | """ 59 | Returns the Final Estimate value of this path 60 | """ 61 | return self._final_estimate 62 | 63 | @property 64 | def sample_idx(self) -> int: 65 | """ 66 | Returns the samples index which indicates the path index 67 | """ 68 | return self._sample_idx 69 | 70 | @property 71 | def path_origin(self) -> Point3f: 72 | """ 73 | Returns the path origin 74 | """ 75 | return self._path_origin 76 | 77 | @property 78 | def path_depth(self) -> int: 79 | """ 80 | Returns the path depth (amount of bounces and containing vertices) 81 | """ 82 | return self._path_depth 83 | 84 | @property 85 | def intersections(self) -> typing.Dict[int, IntersectionData]: 86 | """ 87 | Returns the a dict containing all path vertices 88 | """ 89 | return self._dict_intersections 90 | 91 | @property 92 | def intersection_count(self) -> int: 93 | """ 94 | Returns the amount of vertices (intersections) 95 | """ 96 | return self._intersection_count 97 | 98 | def valid_depth(self) -> bool: 99 | """ 100 | Checks if the path depth is valid 101 | """ 102 | return self._path_depth is not None 103 | 104 | def to_string(self) -> str: 105 | return "SampleIdx = {}\n" \ 106 | "PathDepth = {}\n" \ 107 | "PathOrigin = {}\n" \ 108 | "FinalEstimate = {}\n" \ 109 | "ShowPath = {}\n" \ 110 | "Intersections = {}\n" \ 111 | "IntersectionCount = {}".format(self._sample_idx, self._path_depth, self._path_origin, 112 | self._final_estimate, self._dict_intersections, 113 | self._intersection_count) 114 | -------------------------------------------------------------------------------- /model/pixel_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | import typing 27 | from stream.stream import Stream 28 | from model.path_data import PathData 29 | import numpy as np 30 | import logging 31 | 32 | 33 | class PixelData(object): 34 | 35 | """ 36 | RenderData 37 | Represents information about one pixel. 38 | The data is computed on the server side in the pixel re-rendering step. 39 | Containing all information about all traced paths through this pixel with all user added information. 40 | """ 41 | 42 | def __init__(self): 43 | # {sample_index / path_index : PathData} 44 | self._dict_paths = {} # ordered dict (since Python 3.7) 45 | 46 | def deserialize(self, stream : Stream): 47 | """ 48 | Deserialize a DataView object from the socket stream 49 | """ 50 | sample_count = stream.read_uint() 51 | self._dict_paths.clear() 52 | logging.info("SampleCount: {}".format(sample_count)) 53 | # deserialize the amount of paths which were traced through the selected pixel 54 | for sample in range(sample_count): 55 | path_data = PathData(stream) 56 | # append deserialized path to dict 57 | self._dict_paths[path_data.sample_idx] = path_data 58 | 59 | @property 60 | def dict_paths(self) -> typing.Dict[int, PathData]: 61 | """ 62 | Returns a dict containing all traced paths through the pixel 63 | """ 64 | return self._dict_paths 65 | 66 | def get_indices(self) -> np.ndarray: 67 | """ 68 | Returns all path indices as numpy array 69 | """ 70 | return np.array(list(self._dict_paths.keys())) 71 | 72 | def to_string(self) -> str: 73 | """ 74 | Returns a string with class information 75 | """ 76 | return 'number of paths = {}'.format(len(self._dict_paths)) 77 | 78 | def clear(self): 79 | """ 80 | Clears the data 81 | """ 82 | self._dict_paths.clear() 83 | -------------------------------------------------------------------------------- /model/render_info.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from stream.stream import Stream 27 | 28 | 29 | class RenderInfo(object): 30 | 31 | """ 32 | RenderInfo 33 | Holds general information about the rendered scene, 34 | such as scene name, sample count and the path where the final rendered result is saved 35 | """ 36 | 37 | def __init__(self): 38 | self._renderer_name = None 39 | self._scene_name = None 40 | self._sample_count = None 41 | 42 | def deserialize(self, stream : Stream): 43 | """ 44 | Deserialize a Render Info object from the socket stream 45 | :param stream: SocketStream 46 | :return: 47 | """ 48 | self._renderer_name = self.str_or_not_set(stream.read_string()) 49 | self._scene_name = self.str_or_not_set(stream.read_string()) 50 | self._sample_count = stream.read_uint() 51 | 52 | @staticmethod 53 | def str_or_not_set(s : str) -> str: 54 | """ 55 | Returns the non-empty string or "not set' 56 | """ 57 | if s == "": 58 | return "not set" 59 | else: 60 | return s 61 | 62 | @property 63 | def renderer_name(self) -> str: 64 | """ 65 | Returns the renderer name 66 | """ 67 | return self._renderer_name 68 | 69 | @property 70 | def scene_name(self) -> str: 71 | """ 72 | Returns the scene name 73 | """ 74 | return self._scene_name 75 | 76 | @property 77 | def sample_count(self) -> int: 78 | """ 79 | Returns the amount of used samples to render the scene 80 | """ 81 | if self._sample_count is not None: 82 | return self._sample_count 83 | else: 84 | return 0 85 | 86 | @sample_count.setter 87 | def sample_count(self, sample_count : int): 88 | """ 89 | Setter function, sets the sample count 90 | """ 91 | self._sample_count = sample_count 92 | 93 | def to_string(self) -> str: 94 | """ 95 | Returns a string containing information about the class 96 | """ 97 | return 'renderer_name = {} \n' \ 98 | 'scene_name = {} \n' \ 99 | 'sampleCount = {}'.format(self._renderer_name, 100 | self._scene_name, 101 | self._sample_count) 102 | -------------------------------------------------------------------------------- /model/user_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | import typing 27 | from stream.stream import Stream 28 | 29 | 30 | class UserData(object): 31 | 32 | """ 33 | UserData 34 | Handles general data types which can be added by the user during the path tracing algorithm, 35 | in order to debug the system. 36 | Supported data types boolean, float, double, integer, point2i, point2f, point3i, point3f, color4f and vectors 37 | """ 38 | 39 | def __init__(self, stream : Stream): 40 | # handle default data types 41 | self._data = {} 42 | 43 | num_items = stream.read_uint() 44 | for i in range(num_items): 45 | key = stream.read_string() 46 | type_identifier = stream.read_char() 47 | if ord(type_identifier) > ord('0') and ord(type_identifier) <= ord('9'): 48 | type_identifier = type_identifier+stream.read_char() 49 | type_identifier = type_identifier.decode("utf-8") 50 | 51 | if type_identifier == '?': 52 | self._data[key] = stream.read_bool() 53 | elif type_identifier == 'f': 54 | self._data[key] = stream.read_float() 55 | elif type_identifier == 'd': 56 | self._data[key] = stream.read_double() 57 | elif type_identifier == 'i': 58 | self._data[key] = stream.read_int() 59 | elif type_identifier == '2i': 60 | self._data[key] = stream.read_point2i() 61 | elif type_identifier == '2f': 62 | self._data[key] = stream.read_point2f() 63 | elif type_identifier == '3i': 64 | self._data[key] = stream.read_point3i() 65 | elif type_identifier == '3f': 66 | self._data[key] = stream.read_point3f() 67 | elif type_identifier == '4f': 68 | self._data[key] = stream.read_color4f() 69 | elif type_identifier == 's': 70 | self._data[key] = stream.read_string() 71 | else: 72 | raise Exception('unknown type '+type_identifier) 73 | 74 | @property 75 | def data(self) -> typing.Dict[str, typing.Any]: 76 | """ 77 | Returns a data dict with set information 78 | """ 79 | return self._data 80 | -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from plugins.plugin_intersection_data.plugin_intersection_data_plots import IntersectionData 27 | from plugins.plugin_spherical_view.plugin_spherical_view import SphericalView 28 | 29 | # In order to initialize your plugin, import your plugin here and add it to __all__ list. 30 | 31 | __all__ = [ 32 | 'IntersectionData', 33 | 'SphericalView', 34 | ] 35 | -------------------------------------------------------------------------------- /plugins/plugin_intersection_data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/plugins/plugin_intersection_data/__init__.py -------------------------------------------------------------------------------- /plugins/plugin_intersection_data/intersection_data_plot_2d.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.plot_2d import ScatterPlot2D 27 | import numpy as np 28 | import logging 29 | 30 | 31 | class IntersectionDataPlot2D(ScatterPlot2D): 32 | 33 | def __init__(self, parent=None): 34 | ScatterPlot2D.__init__(self, None) 35 | self.parent = parent 36 | # overwrite pick event since we work on vertices not on paths 37 | self.highlighter.overwrite_pick_event(self.handle_pick) 38 | self.highlighter.enable_multi_selection(False) 39 | 40 | def handle_pick(self, event): 41 | ind = event.ind 42 | 43 | path_idx = self.parent.current_path_index() 44 | x_data_ax1 = np.int32(self.line.get_offsets().data[:,0]) 45 | 46 | self.parent.send_select_intersection(path_idx, x_data_ax1[ind[0]]) 47 | 48 | def select_intersection(self, its_idx): 49 | self.highlight([its_idx]) 50 | 51 | def plot(self, name, values, xmin=None, xmax=None): 52 | x_list = values['its'] 53 | y_list = values['value'] 54 | 55 | self.plot_2d(x_list, y_list) 56 | if xmin is not None and xmax is not None: 57 | self.axes[0].set_xlim(xmin, xmax) 58 | self.axes[0].set_xticks(x_list) 59 | self.axes[0].set_title(name, color=self.color_title) 60 | self.axes[0].set_ylabel(name, color=self.color_title) 61 | #self.axes.set_xlabel('path depth', color=self.color_title) 62 | 63 | -------------------------------------------------------------------------------- /plugins/plugin_intersection_data/intersection_data_plot_3d.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.plot_2d_xy import ScatterPlot2DXY 27 | import logging 28 | 29 | 30 | class IntersectionDataPlot3D(ScatterPlot2DXY): 31 | 32 | def __init__(self, parent=None): 33 | ScatterPlot2DXY.__init__(self, None) 34 | self.parent = parent 35 | # overwrite pick event since we work on vertices not on paths 36 | self.highlighter.overwrite_pick_event(self.handle_pick) 37 | self.highlighter.enable_multi_selection(False) 38 | 39 | def handle_pick(self, event): 40 | ind = event.ind 41 | 42 | path_idx = self.parent.current_path_index() 43 | 44 | self.parent.send_select_intersection(path_idx, self.x[ind[0]]) 45 | 46 | def select_intersection(self, its_idx): 47 | self.highlight([its_idx]) 48 | 49 | def prepare_plot_data(self, values): 50 | x_list = values['its'] 51 | points = values['value'] 52 | 53 | y_list = points[:,0] 54 | z_list = points[:,1] 55 | 56 | return x_list, y_list, z_list 57 | 58 | def plot(self, name, values, xmin=None, xmax=None): 59 | x, y, z = self.prepare_plot_data(values) 60 | self.plot_2d(x, y, z) 61 | #if xmin is not None and xmax is not None: 62 | #self.axes.set_xlim(xmin, xmax) 63 | self.set_title(name) 64 | #self.axes.set_xlabel('path depth', color=self.color_title) 65 | #self.axes.set_ylabel('x', color=self.color_title) 66 | #self.axes.set_zlabel('y', color=self.color_title) 67 | #self.axes.set_xticks(x) 68 | -------------------------------------------------------------------------------- /plugins/plugin_intersection_data/intersection_data_plot_rgb.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.plot_rgb import RGBScatterPlot 27 | import numpy as np 28 | 29 | 30 | class IntersectionDataPlotRGB(RGBScatterPlot): 31 | 32 | def __init__(self, parent=None): 33 | RGBScatterPlot.__init__(self, None) 34 | self.parent = parent 35 | self.highlighter.enable_multi_selection(False) 36 | self.highlighter.overwrite_pick_event(self.handle_pick) 37 | 38 | def handle_pick(self, event): 39 | ind = event.ind 40 | 41 | path_idx = self.parent.current_path_index() 42 | x_data_ax1 = np.int32(self.r_line.get_offsets().data[:,0]) 43 | 44 | self.parent.send_select_intersection(path_idx, x_data_ax1[ind[0]]) 45 | 46 | def select_intersection(self, its_idx): 47 | self.highlight([its_idx]) 48 | 49 | def plot(self, name, values, xmin=None, xmax=None): 50 | x_list = values['its'] 51 | points = values['value'] 52 | 53 | red_list = points[:,0] 54 | green_list = points[:,1] 55 | blue_list = points[:,2] 56 | 57 | self.plot_rgb(x_list, red_list, green_list, blue_list) 58 | for ax in self.figure.axes: 59 | #ax.set_xlabel('path depth', color=self.color_title) 60 | ax.set_xticks(x_list) 61 | ax.set_xlim(xmin, xmax) 62 | -------------------------------------------------------------------------------- /plugins/plugin_intersection_data/plot_list_item.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from PySide2.QtWidgets import QListWidgetItem 27 | 28 | 29 | class PlotListItem(QListWidgetItem): 30 | 31 | def __init__(self, name, list_widget, plot_data, xmin, xmax, plot_canvas, widget_idx): 32 | QListWidgetItem.__init__(self, name, list_widget) 33 | 34 | self._name = name 35 | self._plot_data = plot_data 36 | self._xmin = xmin 37 | self._xmax = xmax 38 | self._plot_canvas = plot_canvas 39 | self._widget_idx = widget_idx 40 | 41 | @property 42 | def name(self): 43 | return self._name 44 | 45 | @property 46 | def plot_data(self): 47 | return self._plot_data 48 | 49 | @property 50 | def xmin(self): 51 | return self._xmin 52 | 53 | @property 54 | def xmax(self): 55 | return self._xmax 56 | 57 | @property 58 | def plot_canvas(self): 59 | return self._plot_canvas 60 | 61 | @property 62 | def widget_idx(self): 63 | return self._widget_idx 64 | -------------------------------------------------------------------------------- /plugins/plugin_intersection_data/ui/plugin_plots.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | uirenderdatahistograms 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 773 13 | 517 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 514 25 | 517 26 | 27 | 28 | 29 | Vertex Data Plots 30 | 31 | 32 | 33 | QLayout::SetMinimumSize 34 | 35 | 36 | 37 | 38 | QLayout::SetMinimumSize 39 | 40 | 41 | 42 | 43 | QLayout::SetMinimumSize 44 | 45 | 46 | 47 | 48 | 49 | 0 50 | 0 51 | 52 | 53 | 54 | QAbstractScrollArea::AdjustToContents 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 0 63 | 0 64 | 65 | 66 | 67 | QAbstractScrollArea::AdjustToContents 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /plugins/plugin_spherical_view/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/plugins/plugin_spherical_view/__init__.py -------------------------------------------------------------------------------- /plugins/plugin_spherical_view/plugin_spherical_view.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.plugin import Plugin, PluginType 27 | from model.pixel_data import PixelData 28 | from plugins.plugin_spherical_view.view_spherical_view_image import ViewSphericalViewImage 29 | from core.point import Point2i 30 | from core.pyside2_uic import loadUi 31 | import io 32 | import os 33 | import logging 34 | 35 | 36 | class SphericalView(Plugin): 37 | 38 | """ 39 | SphericalView Plugins (mitsuba) 40 | PluginType is ServerPlugin since data is generated on server side 41 | """ 42 | 43 | def __init__(self): 44 | Plugin.__init__(self, "SphericalView", 66, PluginType.SERVER_PLUGIN) 45 | ui_filepath = os.path.abspath(os.path.join(os.path.dirname(__file__), 'ui', 'spherical_view.ui')) 46 | loadUi(ui_filepath, self) 47 | 48 | self._path = None 49 | self._pixel_data = None 50 | self._render_size = Point2i(256, 128) 51 | self._sample_count = 16 52 | self._integrator = "path" 53 | 54 | self._spherical_view = ViewSphericalViewImage(self) 55 | self.hdrImage.addWidget(self._spherical_view) 56 | 57 | def apply_theme(self, theme): 58 | pass 59 | 60 | def prepare_new_data(self): 61 | self._spherical_view.clear() 62 | self._spherical_view.enable_buttons(False) 63 | 64 | def update_path_indices(self, indices): 65 | pass 66 | 67 | def select_path(self, index): 68 | pass 69 | 70 | def select_intersection(self, path_idx, its_idx): 71 | if self._pixel_data: 72 | dict_paths = self._pixel_data.dict_paths 73 | self._spherical_view.select_intersection(dict_paths, path_idx, its_idx) 74 | 75 | def serialize(self, stream): 76 | logging.info("Serialize in: {}".format(self.name)) 77 | if self._spherical_view.pos is not None: 78 | self._render_size.x = self.sbWidth.value() 79 | self._render_size.y = self.sbHeight.value() 80 | self._sample_count = int(self.sbSampleCount.value()) 81 | self._integrator = self.integrator.text() 82 | # send package id 83 | stream.write_short(self.flag) 84 | # send point / data 85 | stream.write_float(self._spherical_view.pos.x) 86 | stream.write_float(self._spherical_view.pos.y) 87 | stream.write_float(self._spherical_view.pos.z) 88 | # send amount of samples 89 | stream.write_int(self._sample_count) 90 | # send render size 91 | stream.write_int(self._render_size.x) 92 | stream.write_int(self._render_size.y) 93 | # send integrator 94 | stream.write_string(self._integrator) 95 | 96 | def deserialize(self, stream): 97 | logging.info("Deserialize in: {}".format(self.name)) 98 | size = stream.read_int() 99 | if size > 0: 100 | self._path = io.BytesIO(stream.read(size)) 101 | else: 102 | self._path = None 103 | 104 | def update_view(self): 105 | if self._path is None: 106 | logging.error("Path is None") 107 | return 108 | if self._spherical_view.load_hdr_image(self._path): 109 | if not self._spherical_view.is_btn_enabled: 110 | self._spherical_view.enable_buttons(True) 111 | self._spherical_view.update_hightlights() 112 | 113 | def init_pixel_data(self, pixel_data : PixelData): 114 | self._pixel_data = pixel_data 115 | -------------------------------------------------------------------------------- /plugins/plugin_spherical_view/ui/spherical_view.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | uiSphericalView 4 | 5 | 6 | true 7 | 8 | 9 | Tool Plots 10 | 11 | 12 | 13 | QLayout::SetMinimumSize 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Image width: 24 | 25 | 26 | sbWidth 27 | 28 | 29 | 30 | 31 | 32 | 33 | 1 34 | 35 | 36 | 1920 37 | 38 | 39 | 256 40 | 41 | 42 | 43 | 44 | 45 | 46 | Image height: 47 | 48 | 49 | sbHeight 50 | 51 | 52 | 53 | 54 | 55 | 56 | 1 57 | 58 | 59 | 1080 60 | 61 | 62 | 128 63 | 64 | 65 | 66 | 67 | 68 | 69 | Integrator: 70 | 71 | 72 | integrator 73 | 74 | 75 | 76 | 77 | 78 | 79 | path 80 | 81 | 82 | 83 | 84 | 85 | 86 | SampleCount: 87 | 88 | 89 | sbSampleCount 90 | 91 | 92 | 93 | 94 | 95 | 96 | 1 97 | 98 | 99 | 999999999 100 | 101 | 102 | 16 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | sbWidth 112 | sbHeight 113 | integrator 114 | sbSampleCount 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /plugins/plugin_spherical_view/ui/spherical_view_image.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | uiSphericalView 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 480 13 | 280 14 | 15 | 16 | 17 | 18 | 480 19 | 280 20 | 21 | 22 | 23 | Tool Plots 24 | 25 | 26 | 27 | QLayout::SetMinimumSize 28 | 29 | 30 | 0 31 | 32 | 33 | 0 34 | 35 | 36 | 0 37 | 38 | 39 | 0 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | exposure 50 | 51 | 52 | -500 53 | 54 | 55 | 500 56 | 57 | 58 | Qt::Horizontal 59 | 60 | 61 | QSlider::TicksBelow 62 | 63 | 64 | 100 65 | 66 | 67 | 68 | 69 | 70 | 71 | Falsecolor 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | false 83 | 84 | 85 | Save 86 | 87 | 88 | 89 | 90 | 91 | 92 | false 93 | 94 | 95 | Fit View 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /renderer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/renderer/__init__.py -------------------------------------------------------------------------------- /renderer/rubberband.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | import vtk 27 | import logging 28 | 29 | 30 | class RubberBandInteractor(vtk.vtkInteractorStyleRubberBandPick): 31 | 32 | """ 33 | RubberBandInteractor 34 | Allows a rubber band selector by clicking the 'r' key within the 3D renderer scene view. 35 | The selected vertex and its corresponding path will be automatically be visualized within all other views. 36 | """ 37 | 38 | def __init__(self, parent=None): 39 | super().__init__() 40 | 41 | self.AddObserver("KeyPressEvent", self.key_press_event) 42 | 43 | def key_press_event(self, obj, event): 44 | """ 45 | Handles key input for the 3D scene viewer. 46 | Moves the camera within the scene 47 | :param obj: 48 | :param event: 49 | :return: 50 | """ 51 | key = self.GetInteractor().GetKeySym() 52 | camera = self.GetDefaultRenderer().GetActiveCamera() 53 | 54 | if key == 'Up': 55 | camera.move_forward() 56 | elif key == 'Left': 57 | camera.move_left() 58 | elif key == 'Right': 59 | camera.move_right() 60 | elif key == 'Down': 61 | camera.move_backward() 62 | elif key == '8': 63 | camera.move_up() 64 | elif key == '4': 65 | camera.pan_left() 66 | elif key == '6': 67 | camera.pan_right() 68 | elif key == '2': 69 | camera.move_down() 70 | 71 | self.GetDefaultRenderer().ResetCameraClippingRange() 72 | self.GetInteractor().Render() 73 | -------------------------------------------------------------------------------- /renderer/shape.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.color import Color4f 27 | 28 | import numpy as np 29 | import vtk 30 | 31 | class Shape(vtk.vtkActor): 32 | 33 | def __init__(self, 34 | mesh_poly_data : vtk.vtkPolyData, 35 | color_diffuse : Color4f = Color4f(1, 1, 1), 36 | color_specular : Color4f = Color4f(0, 0, 0)): 37 | super().__init__() 38 | 39 | self._mapper = vtk.vtkPolyDataMapper() 40 | # use setter function - this sets the mapper inputs 41 | self.poly_data = mesh_poly_data 42 | self.SetMapper(self._mapper) 43 | 44 | # set default lighting options 45 | self.GetProperty().SetLighting(True) 46 | self.GetProperty().LightingOn() 47 | self.GetProperty().SetShading(True) 48 | self.GetProperty().ShadingOn() 49 | self.GetProperty().SetDiffuseColor(color_diffuse[0:3]) 50 | self.GetProperty().SetDiffuse(1) 51 | if not np.allclose(color_specular, Color4f(0,0,0)): 52 | self.GetProperty().SetSpecularColor(color_specular[0:3]) 53 | self.GetProperty().SetSpecular(np.mean(np.array(color_specular))/np.mean(np.array(color_specular+color_diffuse))) 54 | self.GetProperty().SetAmbient(0) 55 | 56 | @property 57 | def poly_data(self) -> vtk.vtkPolyData: 58 | return self._poly_data 59 | 60 | @poly_data.setter 61 | def poly_data(self, poly_data : vtk.vtkPolyData): 62 | self._poly_data = poly_data 63 | 64 | if isinstance(self._poly_data, vtk.vtkAlgorithmOutput): 65 | self._mapper.SetInputConnection(self._poly_data) 66 | else: 67 | self._mapper.SetInputData(self._poly_data) 68 | 69 | @property 70 | def mapper(self) -> vtk.vtkPolyDataMapper: 71 | return self._mapper 72 | 73 | @property 74 | def opacity(self) -> float: 75 | """ 76 | Returns the current opacity of the object 77 | """ 78 | return self.GetProperty().GetOpacity() 79 | 80 | @opacity.setter 81 | def opacity(self, value : float): 82 | """ 83 | Sets the opacity of the object 84 | """ 85 | self.GetProperty().SetOpacity(value) 86 | 87 | @property 88 | def color(self) -> Color4f: 89 | """ 90 | Returns the actual color of the object 91 | :return: list 92 | """ 93 | return Color4f(self.GetProperty().GetColor()) 94 | 95 | @color.setter 96 | def color(self, color : Color4f): 97 | """ 98 | Sets the current color of the object 99 | """ 100 | self.GetProperty().SetColor(color) 101 | 102 | @property 103 | def color_diffuse(self) -> Color4f: 104 | """ 105 | Returns the current diffuse color of the object 106 | """ 107 | return Color4f(self.GetProperty().GetDiffuseColor()) 108 | 109 | @color_diffuse.setter 110 | def color_diffuse(self, color_diffuse : Color4f): 111 | """ 112 | Sets the diffuse color of the object 113 | """ 114 | self.GetProperty().SetDiffuseColor(color_diffuse) 115 | 116 | @property 117 | def color_specular(self) -> Color4f: 118 | """ 119 | Return the current specular color of the object 120 | """ 121 | return Color4f(self.GetProperty().GetSpecularColor()) 122 | 123 | @color_specular.setter 124 | def color_specular(self, color_specular : Color4f): 125 | """ 126 | Sets the specular color of the object 127 | """ 128 | self.GetProperty.SetSpecularColor(color_specular) 129 | -------------------------------------------------------------------------------- /renderer/sphere.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from renderer.shape import Shape 27 | import vtk 28 | import logging 29 | 30 | 31 | class Sphere(Shape): 32 | 33 | """ 34 | Sphere 35 | Represents a vtk Sphere object within the 3D scene 36 | """ 37 | 38 | def __init__(self, center, radius, theta_resolution=20, phi_resolution=20): 39 | 40 | self._center = center 41 | self._radius = radius 42 | 43 | sphere = vtk.vtkSphereSource() 44 | # center (x,y,z) 45 | sphere.SetCenter(list(center)) 46 | sphere.SetRadius(radius) 47 | sphere.SetThetaResolution(theta_resolution) 48 | sphere.SetPhiResolution(phi_resolution) 49 | 50 | # get poly data 51 | mesh_poly_data = sphere.GetOutputPort() 52 | 53 | super().__init__(mesh_poly_data) 54 | 55 | @property 56 | def radius(self): 57 | return self._radius 58 | 59 | @radius.setter 60 | def radius(self, radius): 61 | self._radius = radius 62 | 63 | @property 64 | def center(self): 65 | return self._center 66 | 67 | @center.setter 68 | def center(self, center): 69 | self._center = center 70 | 71 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Imath 2 | numpy>=1.18.2 3 | scipy>=1.1.0 4 | matplotlib>=3.2.1 5 | OpenEXR>=1.3.2 6 | Pillow>=7.1.2 7 | PySide2>=5.14.2.1 8 | vtk>=9.0.0 9 | -------------------------------------------------------------------------------- /resources/dark/branch_closed-on.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/branch_closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/branch_open-on.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/branch_open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/checkbox_checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/dark/checkbox_checked_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/dark/checkbox_indeterminate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/dark/checkbox_indeterminate_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/dark/checkbox_unchecked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/checkbox_unchecked_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/close-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/close-pressed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/down_arrow-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/down_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/down_arrow_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/hmovetoolbar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/hsepartoolbar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/left_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/left_arrow_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/radio_checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/dark/radio_checked_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/dark/radio_unchecked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/radio_unchecked_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/right_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/right_arrow_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/sizegrip.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/spinup_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/stylesheet-branch-end-closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/stylesheet-branch-end-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/stylesheet-branch-end.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/stylesheet-branch-more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dark/stylesheet-vline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/transparent.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/dark/undock-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/dark/undock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/up_arrow-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/up_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/up_arrow_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/dark/vmovetoolbar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/dark/vsepartoolbars.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/emca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/resources/emca.png -------------------------------------------------------------------------------- /resources/light/branch_closed-on.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/branch_closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/branch_open-on.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/branch_open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/checkbox_checked-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/light/checkbox_checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/light/checkbox_checked_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/light/checkbox_indeterminate-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/light/checkbox_indeterminate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/light/checkbox_indeterminate_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/light/checkbox_unchecked-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/light/checkbox_unchecked_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/light/close-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/close-pressed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/down_arrow-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/down_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/down_arrow_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/hmovetoolbar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/light/hsepartoolbar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/left_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/left_arrow_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/radio_checked-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/light/radio_checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/light/radio_checked_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/light/radio_unchecked-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/light/radio_unchecked_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/light/right_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/right_arrow_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/sizegrip.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/spinup_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/stylesheet-branch-end-closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/light/stylesheet-branch-end-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/light/stylesheet-branch-end.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/light/stylesheet-branch-more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/light/stylesheet-vline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/transparent.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/light/undock-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/light/undock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/up_arrow-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/up_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/up_arrow_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/light/vmovetoolbar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/light/vsepartoolbars.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/options.ini: -------------------------------------------------------------------------------- 1 | [Theme] 2 | theme = dark 3 | 4 | [Options] 5 | auto_connect = True 6 | auto_scene_load = True 7 | auto_rendered_image_load = True 8 | 9 | [Last] 10 | hostname = localhost 11 | port = 50013 12 | 13 | -------------------------------------------------------------------------------- /server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.2) 2 | project(emca 3 | DESCRIPTION "Explorer of Monte Carlo based Algorithms (Server)" 4 | LANGUAGES CXX) 5 | 6 | # Set a default build configuration (Release) 7 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 8 | message(STATUS "Setting build type to 'Release' as none was specified.") 9 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) 10 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" 11 | "MinSizeRel" "RelWithDebInfo") 12 | endif() 13 | 14 | include(GNUInstallDirs) 15 | 16 | set(CMAKE_CXX_STANDARD 17) 17 | 18 | option(EMCA_BUILD_SHARED "Build EMCA as a shared library?" ON) 19 | 20 | # Set library type 21 | if (EMCA_BUILD_SHARED) 22 | set(EMCA_LIBRARY_TYPE "SHARED") 23 | else() 24 | set(EMCA_LIBRARY_TYPE "STATIC") 25 | endif() 26 | 27 | add_library(${PROJECT_NAME} ${EMCA_LIBRARY_TYPE} 28 | src/emcaserver.cpp 29 | src/dataapi.cpp 30 | src/pathdata.cpp 31 | src/heatmapdata.cpp) 32 | 33 | # atomic library needed for 16 byte atomic read/write 34 | target_link_libraries(${PROJECT_NAME} PUBLIC atomic) 35 | 36 | target_include_directories(${PROJECT_NAME} PUBLIC 37 | $ 38 | $ 39 | PRIVATE src) 40 | 41 | set_target_properties(${PROJECT_NAME} PROPERTIES 42 | VERSION "1.0.0" 43 | SOVERSION 1) 44 | 45 | install(TARGETS ${PROJECT_NAME} EXPORT EMCAConfig 46 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 47 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 48 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 49 | 50 | install(DIRECTORY include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 51 | install(EXPORT EMCAConfig DESTINATION share/EMCALib/cmake) 52 | 53 | export(TARGETS ${PROJECT_NAME} FILE EMCAConfig.cmake) 54 | -------------------------------------------------------------------------------- /server/LICENSE: -------------------------------------------------------------------------------- 1 | Licensed to the Apache Software Foundation (ASF) under one 2 | or more contributor license agreements. See the NOTICE file 3 | distributed with this work for additional information 4 | regarding copyright ownership. The ASF licenses this file 5 | to you under the Apache License, Version 2.0 (the 6 | "License"); you may not use this file except in compliance 7 | with the License. You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, 12 | software distributed under the License is distributed on an 13 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | KIND, either express or implied. See the License for the 15 | specific language governing permissions and limitations 16 | under the License. -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # EMCA - Server Library 4 | 5 | 6 | 7 | ## About 8 | EMCA comes with a server and client part. All files within this folder represents the server part which must be connected to the specific render system. The server files are compiled as shared library which can be then easily included into your project. 9 | 10 | ## Table of contents 11 | * [About](#about) 12 | * [Build](#build) 13 | * [License](#license) 14 | 15 | 16 | 17 | ## Build 18 | For compiling the shared library we provide a CMakeLists.txt file. 19 | 20 | ``` 21 | mkdir build 22 | cd build 23 | cmake .. 24 | make -j4 25 | sudo make install 26 | ``` 27 | 28 | Make sure that the path to the emca shared library is defined in **LD_LIBRARY_PATH**. 29 | 30 | 31 | 32 | ## License 33 | The shared library comes with the Apache License 2.0. 34 | 35 | (c) Christoph Kreisl 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /server/include/emca/datatypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | EMCA - Explorer of Monte Carlo based Alorithms (Shared Server Library) 3 | comes with an Apache License 2.0 4 | (c) Christoph Kreisl 2020 5 | (c) Lukas Ruppert 2021 6 | 7 | Licensed to the Apache Software Foundation (ASF) under one 8 | or more contributor license agreements. See the NOTICE file 9 | distributed with this work for additional information 10 | regarding copyright ownership. The ASF licenses this file 11 | to you under the Apache License, Version 2.0 (the 12 | "License"); you may not use this file except in compliance 13 | with the License. You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, 18 | software distributed under the License is distributed on an 19 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | KIND, either express or implied. See the License for the 21 | specific language governing permissions and limitations 22 | under the License. 23 | */ 24 | 25 | #ifndef INCLUDE_EMCA_DATATYPES_H_ 26 | #define INCLUDE_EMCA_DATATYPES_H_ 27 | 28 | #include "platform.h" 29 | #include "stream.h" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | EMCA_NAMESPACE_BEGIN 37 | 38 | template struct TArray { 39 | static constexpr size_t dim = N; 40 | std::array p {}; 41 | 42 | std::enable_if_t<(dim > 0), T> x() const { return p[0]; } 43 | std::enable_if_t<(dim > 1), T> y() const { return p[1]; } 44 | std::enable_if_t<(dim > 2), T> z() const { return p[2]; } 45 | 46 | std::enable_if_t<(dim > 0), T> norm() const { return std::sqrt(std::accumulate(p.begin(), p.end(), T(0), [](T sum, T element) -> T { return sum+element*element; })); } 47 | std::enable_if_t<(dim == 3), T> dot(const TArray& other) const { return x()*other.x()+y()*other.y()+z()*other.z(); } 48 | 49 | void serialize(Stream *stream) const { 50 | stream->writeArray(p.data(), dim); 51 | } 52 | }; 53 | 54 | template struct TPoint2 : public TArray { 55 | using TArray::dim; 56 | using TArray::p; 57 | 58 | TPoint2() = default; 59 | TPoint2(T x, T y) { 60 | p[0] = x; 61 | p[y] = y; 62 | } 63 | }; 64 | 65 | template struct TVec3 : public TArray { 66 | using TArray::dim; 67 | using TArray::p; 68 | 69 | TVec3() = default; 70 | TVec3(T x, T y, T z) { 71 | p[0] = x; 72 | p[1] = y; 73 | p[2] = z; 74 | } 75 | 76 | TVec3& operator*=(T scale) { p[0]*=scale; p[1]*=scale; p[2]*=scale; return *this; } 77 | T normalize() { const float norm = this->norm(); *this *= 1.0f/norm; return norm; } 78 | TVec3 operator*(T scale) { TVec3 copy{*this}; copy*=scale; return copy; } 79 | }; 80 | 81 | template 82 | TVec3 cross(const TVec3& a, const TVec3& b) { 83 | return {a.y()*b.z()-a.z()*b.y(), a.z()*b.x()-a.x()*b.z(), a.x()*b.y()-a.y()*b.x()}; 84 | } 85 | 86 | template struct TPoint3 : public TArray { 87 | using TArray::dim; 88 | using TArray::p; 89 | using TArray::x; 90 | using TArray::y; 91 | using TArray::z; 92 | 93 | TPoint3() = default; 94 | TPoint3(T x, T y, T z) { 95 | p[0] = x; 96 | p[1] = y; 97 | p[2] = z; 98 | } 99 | 100 | TVec3 operator-(const TPoint3& other) const { 101 | return TVec3{x()-other.x(), y()-other.y(), z()-other.z()}; 102 | } 103 | TPoint3 operator+(const TVec3& other) const { 104 | return TPoint3{x()+other.x(), y()+other.y(), z()+other.z()}; 105 | } 106 | }; 107 | 108 | template struct TColor4 { 109 | static constexpr int dim = 4; 110 | std::array c {}; 111 | 112 | TColor4() = default; 113 | TColor4(T r, T g, T b, T a=1) : c{r, g, b, a} {} 114 | 115 | T r() const { return c[0]; } 116 | T g() const { return c[1]; } 117 | T b() const { return c[2]; } 118 | T a() const { return c[3]; } 119 | 120 | void serialize(Stream *stream) const { 121 | stream->writeArray(c.data(), dim); 122 | } 123 | }; 124 | 125 | typedef TPoint2 Point2i; 126 | typedef TPoint2 Point2f; 127 | typedef TPoint3 Point3i; 128 | typedef TPoint3 Point3f; 129 | typedef TVec3 Vec3u; 130 | typedef TVec3 Vec3f; 131 | typedef TColor4 Color4f; 132 | 133 | EMCA_NAMESPACE_END 134 | 135 | #endif /* INCLUDE_EMCA_DATATYPES_H_ */ 136 | -------------------------------------------------------------------------------- /server/include/emca/emcaserver.h: -------------------------------------------------------------------------------- 1 | /* 2 | EMCA - Explorer of Monte Carlo based Alorithms (Shared Server Library) 3 | comes with an Apache License 2.0 4 | (c) Christoph Kreisl 2020 5 | (c) Lukas Ruppert 2021 6 | 7 | Licensed to the Apache Software Foundation (ASF) under one 8 | or more contributor license agreements. See the NOTICE file 9 | distributed with this work for additional information 10 | regarding copyright ownership. The ASF licenses this file 11 | to you under the Apache License, Version 2.0 (the 12 | "License"); you may not use this file except in compliance 13 | with the License. You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, 18 | software distributed under the License is distributed on an 19 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | KIND, either express or implied. See the License for the 21 | specific language governing permissions and limitations 22 | under the License. 23 | */ 24 | 25 | #ifndef INCLUDE_EMCA_EMCASERVER_H_ 26 | #define INCLUDE_EMCA_EMCASERVER_H_ 27 | 28 | #include 29 | 30 | #include "platform.h" 31 | #include "stream.h" 32 | #include "renderinterface.h" 33 | #include "dataapi.h" 34 | #include "plugin.h" 35 | #include "stream.h" 36 | 37 | #include 38 | 39 | EMCA_NAMESPACE_BEGIN 40 | 41 | class EMCAServer { 42 | public: 43 | EMCAServer(RenderInterface* renderer, DataApi* dataApi); 44 | ~EMCAServer() { stop(); } 45 | 46 | /// runs the main TCP server that communicates with the client. 47 | /// does not return until the server is shut down. 48 | void run(uint16_t port=50013); 49 | 50 | /// disconnect the current client 51 | void disconnect(); 52 | /// stop the TCP server 53 | void stop(); 54 | 55 | private: 56 | // implementation of the binary protocol between server and client 57 | // changes made here require similar changes on the client side 58 | // these functions call into the renderer where necessary to provide the requested data 59 | void respondSupportedPlugins(); 60 | void respondRenderInfo(); 61 | void respondRenderImage(); 62 | void respondCameraData(); 63 | void respondSceneData(); 64 | void respondRenderPixel(); 65 | bool respondPluginRequest(short id); 66 | 67 | RenderInterface* m_renderer {nullptr}; 68 | DataApi* m_dataApi {nullptr}; 69 | 70 | typedef int socket_t; 71 | 72 | class SocketStream final : public Stream { 73 | public: 74 | SocketStream(socket_t client_socket) : m_clientSocket(client_socket) {} 75 | 76 | private: 77 | void write(const void *ptr, size_t size); 78 | void read(void *ptr, size_t size); 79 | 80 | socket_t m_clientSocket; // not owned - will not be closed on destruction 81 | }; 82 | 83 | socket_t m_clientSocket {-1}; 84 | socket_t m_serverSocket {-1}; 85 | std::unique_ptr m_stream; 86 | 87 | std::vector m_mesh_data; 88 | }; 89 | 90 | EMCA_NAMESPACE_END 91 | 92 | #endif /* INCLUDE_EMCA_EMCASERVER_H_ */ 93 | -------------------------------------------------------------------------------- /server/include/emca/messages.h: -------------------------------------------------------------------------------- 1 | /* 2 | EMCA - Explorer of Monte Carlo based Alorithms (Shared Server Library) 3 | comes with an Apache License 2.0 4 | (c) Christoph Kreisl 2020 5 | (c) Lukas Ruppert 2021 6 | 7 | Licensed to the Apache Software Foundation (ASF) under one 8 | or more contributor license agreements. See the NOTICE file 9 | distributed with this work for additional information 10 | regarding copyright ownership. The ASF licenses this file 11 | to you under the Apache License, Version 2.0 (the 12 | "License"); you may not use this file except in compliance 13 | with the License. You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, 18 | software distributed under the License is distributed on an 19 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | KIND, either express or implied. See the License for the 21 | specific language governing permissions and limitations 22 | under the License. 23 | */ 24 | 25 | #ifndef INCLUDE_EMCA_MESSAGES_H_ 26 | #define INCLUDE_EMCA_MESSAGES_H_ 27 | 28 | #include "platform.h" 29 | 30 | EMCA_NAMESPACE_BEGIN 31 | 32 | // message identifiers for the TCP protocol 33 | enum Message { 34 | // connection management (0x000x) 35 | EMCA_HELLO = 0x0001, 36 | EMCA_SUPPORTED_PLUGINS = 0x0002, 37 | EMCA_DISCONNECT = 0x000E, 38 | EMCA_QUIT = 0x000F, 39 | 40 | // requests from the client (0x001x) 41 | EMCA_REQUEST_RENDER_INFO = 0x0011, 42 | EMCA_REQUEST_RENDER_IMAGE = 0x0012, 43 | EMCA_REQUEST_RENDER_PIXEL = 0x0013, 44 | EMCA_REQUEST_CAMERA = 0x0014, 45 | EMCA_REQUEST_SCENE = 0x0015, 46 | 47 | // responses to the client (0x002x) 48 | EMCA_RESPONSE_RENDER_INFO = 0x0021, 49 | EMCA_RESPONSE_RENDER_IMAGE = 0x0022, 50 | EMCA_RESPONSE_RENDER_PIXEL = 0x0023, 51 | EMCA_RESPONSE_CAMERA = 0x0024, 52 | EMCA_RESPONSE_SCENE = 0x0025, 53 | }; 54 | 55 | // shape types that can be transferred to the client 56 | enum ShapeType 57 | { 58 | TriangleMesh = 0, 59 | SphereMesh = 1 60 | }; 61 | 62 | EMCA_NAMESPACE_END 63 | 64 | #endif /* INCLUDE_EMCA_MESSAGES_H_ */ 65 | -------------------------------------------------------------------------------- /server/include/emca/platform.h: -------------------------------------------------------------------------------- 1 | /* 2 | EMCA - Explorer of Monte Carlo based Alorithms (Shared Server Library) 3 | comes with an Apache License 2.0 4 | (c) Christoph Kreisl 2020 5 | (c) Lukas Ruppert 2021 6 | 7 | Licensed to the Apache Software Foundation (ASF) under one 8 | or more contributor license agreements. See the NOTICE file 9 | distributed with this work for additional information 10 | regarding copyright ownership. The ASF licenses this file 11 | to you under the Apache License, Version 2.0 (the 12 | "License"); you may not use this file except in compliance 13 | with the License. You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, 18 | software distributed under the License is distributed on an 19 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | KIND, either express or implied. See the License for the 21 | specific language governing permissions and limitations 22 | under the License. 23 | */ 24 | 25 | #ifndef INCLUDE_EMCA_PLATFORM_H_ 26 | #define INCLUDE_EMCA_PLATFORM_H_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define EMCA_NAMESPACE_BEGIN namespace emca { 37 | #define EMCA_NAMESPACE_END } 38 | 39 | #endif /* INCLUDE_EMCA_PLATFORM_H_ */ 40 | -------------------------------------------------------------------------------- /server/include/emca/plugin.h: -------------------------------------------------------------------------------- 1 | /* 2 | EMCA - Explorer of Monte Carlo based Alorithms (Shared Server Library) 3 | comes with an Apache License 2.0 4 | (c) Christoph Kreisl 2020 5 | (c) Lukas Ruppert 2021 6 | 7 | Licensed to the Apache Software Foundation (ASF) under one 8 | or more contributor license agreements. See the NOTICE file 9 | distributed with this work for additional information 10 | regarding copyright ownership. The ASF licenses this file 11 | to you under the Apache License, Version 2.0 (the 12 | "License"); you may not use this file except in compliance 13 | with the License. You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, 18 | software distributed under the License is distributed on an 19 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | KIND, either express or implied. See the License for the 21 | specific language governing permissions and limitations 22 | under the License. 23 | */ 24 | 25 | #ifndef INCLUDE_EMCA_PLUGIN_H_ 26 | #define INCLUDE_EMCA_PLUGIN_H_ 27 | 28 | #include "platform.h" 29 | #include "stream.h" 30 | 31 | EMCA_NAMESPACE_BEGIN 32 | 33 | class Plugin { 34 | public: 35 | Plugin(std::string name, short id) : m_name(name), m_id(id) { } 36 | virtual ~Plugin() = default; 37 | 38 | virtual void run() = 0; 39 | virtual void serialize(Stream *stream) const = 0; 40 | virtual void deserialize(Stream *stream) = 0; 41 | 42 | const std::string& getName() const { return m_name; } 43 | int16_t getId() const { return m_id; } 44 | 45 | private: 46 | std::string m_name; 47 | int16_t m_id; 48 | }; 49 | 50 | EMCA_NAMESPACE_END 51 | 52 | #endif /* INCLUDE_EMCA_PLUGIN_H_ */ 53 | -------------------------------------------------------------------------------- /server/include/emca/renderinterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | EMCA - Explorer of Monte Carlo based Alorithms (Shared Server Library) 3 | comes with an Apache License 2.0 4 | (c) Christoph Kreisl 2020 5 | (c) Lukas Ruppert 2021 6 | 7 | Licensed to the Apache Software Foundation (ASF) under one 8 | or more contributor license agreements. See the NOTICE file 9 | distributed with this work for additional information 10 | regarding copyright ownership. The ASF licenses this file 11 | to you under the Apache License, Version 2.0 (the 12 | "License"); you may not use this file except in compliance 13 | with the License. You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, 18 | software distributed under the License is distributed on an 19 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | KIND, either express or implied. See the License for the 21 | specific language governing permissions and limitations 22 | under the License. 23 | */ 24 | 25 | #ifndef EMCA_INCLUDE_RENDERINTERFACE_H_ 26 | #define EMCA_INCLUDE_RENDERINTERFACE_H_ 27 | 28 | #include "platform.h" 29 | #include "scenedata.h" 30 | 31 | EMCA_NAMESPACE_BEGIN 32 | 33 | class RenderInterface { 34 | public: 35 | virtual ~RenderInterface() = default; 36 | virtual void renderImage() = 0; 37 | virtual void renderPixel(uint32_t x, uint32_t y) = 0; 38 | virtual std::string getRendererName() const = 0; 39 | virtual std::string getSceneName() const = 0; 40 | virtual uint32_t getSampleCount() const = 0; 41 | virtual void setSampleCount(size_t sampleCount) = 0; 42 | virtual Camera getCameraData() const = 0; 43 | virtual std::vector getMeshData() const = 0; 44 | virtual std::string getRenderedImagePath() const = 0; 45 | }; 46 | 47 | EMCA_NAMESPACE_END 48 | 49 | #endif /* EMCA_INCLUDE_EMCARENDERINTERFACE_H_ */ 50 | -------------------------------------------------------------------------------- /server/include/emca/scenedata.h: -------------------------------------------------------------------------------- 1 | /* 2 | EMCA - Explorer of Monte Carlo based Alorithms (Shared Server Library) 3 | comes with an Apache License 2.0 4 | (c) Christoph Kreisl 2020 5 | (c) Lukas Ruppert 2021 6 | 7 | Licensed to the Apache Software Foundation (ASF) under one 8 | or more contributor license agreements. See the NOTICE file 9 | distributed with this work for additional information 10 | regarding copyright ownership. The ASF licenses this file 11 | to you under the Apache License, Version 2.0 (the 12 | "License"); you may not use this file except in compliance 13 | with the License. You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, 18 | software distributed under the License is distributed on an 19 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | KIND, either express or implied. See the License for the 21 | specific language governing permissions and limitations 22 | under the License. 23 | */ 24 | 25 | #ifndef INCLUDE_EMCA_EMCADATA_H_ 26 | #define INCLUDE_EMCA_EMCADATA_H_ 27 | 28 | #include "platform.h" 29 | #include "datatypes.h" 30 | #include "stream.h" 31 | #include "messages.h" 32 | 33 | EMCA_NAMESPACE_BEGIN 34 | 35 | struct Camera 36 | { 37 | Point3f origin; 38 | Vec3f dir; 39 | Vec3f up; 40 | float nearClip; 41 | float farClip; 42 | float fov; 43 | 44 | Camera() = default; 45 | Camera(Point3f _origin, Vec3f _dir, Vec3f _up, 46 | float _nearClip, float _farClip, float _fov) 47 | : origin(_origin), dir(_dir), up(_up), 48 | nearClip(_nearClip), farClip(_farClip), fov(_fov) {} 49 | 50 | void serialize(Stream *stream) const 51 | { 52 | origin.serialize(stream); 53 | dir.serialize(stream); 54 | up.serialize(stream); 55 | stream->writeFloat(nearClip); 56 | stream->writeFloat(farClip); 57 | stream->writeFloat(fov); 58 | } 59 | }; 60 | 61 | struct Mesh 62 | { 63 | std::vector vertices; 64 | std::vector triangles; 65 | std::vector faceColors; // optional, leave empty if not used, otherwise provide one value per face 66 | Color4f diffuseColor; 67 | Color4f specularColor; 68 | float surfaceArea {0.0f}; // for heatmap subdivision budget only - not transferred to the EMCA client 69 | 70 | void serialize(Stream *stream) const 71 | { 72 | if (faceColors.size() && faceColors.size() != triangles.size()) 73 | throw std::logic_error("the number of face colors does not match the number of faces"); 74 | 75 | stream->writeShort(ShapeType::TriangleMesh); 76 | stream->writeUInt(vertices.size()); 77 | stream->writeArray(reinterpret_cast(vertices.data()), vertices.size() * 3); 78 | stream->writeUInt(triangles.size()); 79 | stream->writeArray(reinterpret_cast(triangles.data()), triangles.size() * 3); 80 | stream->writeUInt(faceColors.size()); 81 | stream->writeArray(reinterpret_cast(faceColors.data()), faceColors.size() * 3); 82 | diffuseColor.serialize(stream); 83 | specularColor.serialize(stream); 84 | } 85 | }; 86 | 87 | struct Sphere 88 | { 89 | Point3f center; 90 | float radius; 91 | Color4f diffuse; 92 | Color4f specular; 93 | 94 | Sphere() = default; 95 | Sphere(Point3f _center, float _radius) : center(_center), radius(_radius) {} 96 | 97 | void serialize(Stream *stream) const 98 | { 99 | stream->writeShort(ShapeType::SphereMesh); 100 | stream->writeFloat(radius); 101 | center.serialize(stream); 102 | diffuse.serialize(stream); 103 | specular.serialize(stream); 104 | } 105 | }; 106 | 107 | EMCA_NAMESPACE_END 108 | 109 | #endif // INCLUDE_EMCA_EMCADATA_H_ 110 | -------------------------------------------------------------------------------- /server/include/emca/stream.h: -------------------------------------------------------------------------------- 1 | /* 2 | EMCA - Explorer of Monte Carlo based Alorithms (Shared Server Library) 3 | comes with an Apache License 2.0 4 | (c) Christoph Kreisl 2020 5 | (c) Lukas Ruppert 2021 6 | 7 | Licensed to the Apache Software Foundation (ASF) under one 8 | or more contributor license agreements. See the NOTICE file 9 | distributed with this work for additional information 10 | regarding copyright ownership. The ASF licenses this file 11 | to you under the Apache License, Version 2.0 (the 12 | "License"); you may not use this file except in compliance 13 | with the License. You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, 18 | software distributed under the License is distributed on an 19 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | KIND, either express or implied. See the License for the 21 | specific language governing permissions and limitations 22 | under the License. 23 | */ 24 | 25 | #ifndef INCLUDE_EMCA_STREAM_H 26 | #define INCLUDE_EMCA_STREAM_H 27 | 28 | #include "platform.h" 29 | #include 30 | #include 31 | 32 | EMCA_NAMESPACE_BEGIN 33 | 34 | class Stream { 35 | public: 36 | // generic read/write functions for fundamental data types 37 | template, int> = 0> 38 | T read() { 39 | T result; 40 | read(&result, sizeof(T)); 41 | return result; 42 | } 43 | 44 | template, int> = 0> 45 | void write(T value) { 46 | write(&value, sizeof(T)); 47 | } 48 | 49 | // general write functions for arrays of fundamental data types 50 | template , int> = 0> 51 | void writeArray(const T *array, size_t count) { 52 | write(array, sizeof(T)*count); 53 | } 54 | template , int> = 0> 55 | inline void writeArray(const T (&arr)[N]) { 56 | writeArray(&arr[0], N); 57 | } 58 | 59 | // convenience read/write functions for fundamental data types 60 | void writeBool(bool value) {write(char(value));} 61 | void writeShort(int16_t value) {write(value);} 62 | void writeUShort(uint16_t value) {write(value);} 63 | void writeInt(int32_t value) {write(value);} 64 | void writeUInt(uint32_t value) {write(value);} 65 | void writeLong(int64_t value) {write(value);} 66 | void writeULong(uint64_t value) {write(value);} 67 | void writeChar(char value) {write(value);} 68 | void writeUChar(unsigned char value) {write(value);} 69 | void writeFloat(float value) {write(value);} 70 | void writeDouble(double value) {write(value);} 71 | 72 | bool readBool() {return bool(read());} 73 | int16_t readShort() {return read();} 74 | uint16_t readUShort() {return read();} 75 | int32_t readInt() {return read();} 76 | uint32_t readUInt() {return read();} 77 | int64_t readLong() {return read();} 78 | uint64_t readULong() {return read();} 79 | char readChar() {return read();} 80 | unsigned char readUChar() {return read();} 81 | float readFloat() {return read();} 82 | double readDouble() {return read();} 83 | 84 | // read string prefixed by length 85 | std::string readString() { 86 | size_t length = readULong(); 87 | std::string buffer(length, '\0'); 88 | read(buffer.data(), length); 89 | return buffer; 90 | } 91 | 92 | // write string prefixed by length 93 | void writeString(const std::string& value) { 94 | writeULong(value.length()); 95 | write(value.c_str(), sizeof(char) * value.length()); 96 | } 97 | 98 | protected: 99 | virtual ~Stream() = default; 100 | 101 | private: 102 | // abstract base - these need to be implemented by the specific implementation 103 | virtual void read(void *ptr, size_t size) = 0; 104 | virtual void write(const void *ptr, size_t size) = 0; 105 | }; 106 | 107 | EMCA_NAMESPACE_END 108 | 109 | #endif /* INCLUDE_EMCA_STREAM_H */ 110 | -------------------------------------------------------------------------------- /stream/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/stream/__init__.py -------------------------------------------------------------------------------- /view/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/view/__init__.py -------------------------------------------------------------------------------- /view/ui/connect.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | connect 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 269 13 | 109 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 0 25 | 0 26 | 27 | 28 | 29 | Connect Settings 30 | 31 | 32 | 33 | QLayout::SetMinimumSize 34 | 35 | 36 | 37 | 38 | 39 | 40 | Hostname: 41 | 42 | 43 | 44 | 45 | 46 | 47 | Port: 48 | 49 | 50 | 51 | 52 | 53 | 54 | localhost 55 | 56 | 57 | 58 | 59 | 60 | 61 | 50013 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Connect 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /view/ui/options.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | connect 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 296 13 | 193 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 0 25 | 0 26 | 27 | 28 | 29 | Options 30 | 31 | 32 | 33 | QLayout::SetMinimumSize 34 | 35 | 36 | 37 | 38 | 39 | 40 | Theme: 41 | 42 | 43 | 44 | 45 | 46 | 47 | Auto connect on start: 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Auto load 3D scene: 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Auto load last image: 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | dark (default) 90 | 91 | 92 | 93 | 94 | 95 | 96 | light 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | Qt::Horizontal 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Save 115 | 116 | 117 | 118 | 119 | 120 | 121 | Close 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /view/ui/pixel_data.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | uirenderdata 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 500 13 | 500 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 500 25 | 500 26 | 27 | 28 | 29 | Visual Debugger 30 | 31 | 32 | 33 | QLayout::SetMinimumSize 34 | 35 | 36 | 37 | 38 | QFrame::StyledPanel 39 | 40 | 41 | 1 42 | 43 | 44 | 0 45 | 46 | 47 | 48 | 16 49 | 16 50 | 51 | 52 | 53 | 20 54 | 55 | 56 | 2 57 | 58 | 59 | false 60 | 61 | 62 | true 63 | 64 | 65 | 66 | 1 67 | 68 | 69 | 70 | 71 | 2 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | Qt::Horizontal 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | false 89 | 90 | 91 | Show all 92 | 93 | 94 | 95 | 96 | 97 | 98 | false 99 | 100 | 101 | Inspect current selected path 102 | 103 | 104 | Inspect 105 | 106 | 107 | 108 | 109 | 110 | 111 | false 112 | 113 | 114 | Expand All Items 115 | 116 | 117 | 118 | 119 | 120 | 121 | Qt::Horizontal 122 | 123 | 124 | 125 | 40 126 | 20 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /view/ui/render_info.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | uirenderoptions 4 | 5 | 6 | Qt::NonModal 7 | 8 | 9 | true 10 | 11 | 12 | 13 | 0 14 | 0 15 | 299 16 | 134 17 | 18 | 19 | 20 | 21 | 0 22 | 0 23 | 24 | 25 | 26 | 27 | 0 28 | 0 29 | 30 | 31 | 32 | Render Options 33 | 34 | 35 | 36 | QLayout::SetFixedSize 37 | 38 | 39 | 40 | 41 | 42 | 43 | Renderer: 44 | 45 | 46 | 47 | 48 | 49 | 50 | not set 51 | 52 | 53 | 54 | 55 | 56 | 57 | Scene: 58 | 59 | 60 | 61 | 62 | 63 | 64 | not set 65 | 66 | 67 | 68 | 69 | 70 | 71 | Sample Count: 72 | 73 | 74 | 75 | 76 | 77 | 78 | 999999999 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | Close 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /view/ui/render_scene.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | uiscenedata 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 500 13 | 500 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 500 25 | 500 26 | 27 | 28 | 29 | EMCA 30 | 31 | 32 | 33 | QLayout::SetDefaultConstraint 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Qt::Horizontal 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | false 51 | 52 | 53 | Load Scene 54 | 55 | 56 | 57 | 58 | 59 | 60 | false 61 | 62 | 63 | Scene Options 64 | 65 | 66 | 67 | 68 | 69 | 70 | false 71 | 72 | 73 | Reset Camera 74 | 75 | 76 | 77 | 78 | 79 | 80 | Qt::Horizontal 81 | 82 | 83 | 84 | 40 85 | 20 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /view/view_main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/view/view_main/__init__.py -------------------------------------------------------------------------------- /view/view_main/pixel_icon.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from PySide2.QtCore import QPoint 27 | from PySide2.QtGui import QPixmap 28 | from PySide2.QtGui import QColor 29 | from PySide2.QtGui import QIcon 30 | 31 | 32 | class PixelIcon(QIcon): 33 | 34 | """ 35 | PixelIcon 36 | Represents and holds information about a selected pixel 37 | Pixel color, the position. Is used to display the selected pixel as icon in the view 38 | """ 39 | 40 | def __init__(self, color : QColor, pos : QPoint): 41 | super().__init__() 42 | self._pos = pos 43 | self._color = color 44 | pixmap = QPixmap(16,16) 45 | pixmap.fill(self._color) 46 | self.addPixmap(pixmap) 47 | 48 | @property 49 | def pos(self) -> QPoint: 50 | return self._pos 51 | 52 | @property 53 | def color(self) -> QColor: 54 | return self._color 55 | 56 | def __str__(self) -> str: 57 | return '({},{})'.format(self._pos.x(), self._pos.y()) 58 | -------------------------------------------------------------------------------- /view/view_main/view_connect_settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from PySide2.QtGui import QKeyEvent 27 | from core.pyside2_uic import loadUi 28 | from PySide2.QtCore import Qt 29 | from PySide2.QtCore import Slot 30 | from PySide2.QtWidgets import QWidget 31 | from PySide2.QtWidgets import QApplication 32 | import os 33 | import logging 34 | 35 | from typing import TYPE_CHECKING 36 | if TYPE_CHECKING: 37 | from controller.controller import Controller 38 | else: 39 | from typing import Any as Controller 40 | 41 | 42 | class ViewConnectSettings(QWidget): 43 | 44 | """ 45 | ViewConnectSettings 46 | Handles the connect settings view 47 | """ 48 | 49 | def __init__(self, parent=None): 50 | QWidget.__init__(self, parent=None) 51 | ui_filepath = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'ui', 'connect.ui')) 52 | loadUi(ui_filepath, self) 53 | 54 | self._controller = None 55 | 56 | # center widget depending on screen size 57 | desktop_widget = QApplication.desktop() 58 | screen_rect = desktop_widget.availableGeometry(self) 59 | self.move(screen_rect.center() - self.rect().center()) 60 | 61 | self.btnConnect.clicked.connect(self.btn_connect) 62 | 63 | def set_controller(self, controller : Controller): 64 | """ 65 | Set the connection to the controller 66 | """ 67 | self._controller = controller 68 | 69 | @property 70 | def hostname(self) -> str: 71 | """ 72 | Returns the current set hostname 73 | """ 74 | return self.leHostname.text() 75 | 76 | @hostname.setter 77 | def hostname(self, hostname : str): 78 | """ 79 | Set the hostname 80 | """ 81 | self.leHostname.setText(hostname) 82 | 83 | @property 84 | def port(self) -> int: 85 | """ 86 | Returns the current set port 87 | """ 88 | return int(self.lePort.text()) 89 | 90 | @port.setter 91 | def port(self, port : int): 92 | """ 93 | Set the port 94 | """ 95 | self.lePort.setText(str(port)) 96 | 97 | def keyPressEvent(self, event : QKeyEvent): 98 | """ 99 | Handles key press event. If the enter button is clicked the connect button is triggered 100 | """ 101 | if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: 102 | self.btn_connect(True) 103 | 104 | @Slot(bool, name='connect') 105 | def btn_connect(self, clicked : bool): 106 | """ 107 | Informs the controller to connect to the given hostname and port. 108 | Closes the view afterwards. 109 | """ 110 | logging.info('Clicked connect btn') 111 | 112 | if self._controller.stream.connect_socket_stream(self.hostname, self.port): 113 | self.close() 114 | 115 | -------------------------------------------------------------------------------- /view/view_main/view_options_settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.pyside2_uic import loadUi 27 | from PySide2.QtCore import Qt 28 | from PySide2.QtCore import Slot 29 | from PySide2.QtWidgets import QButtonGroup, QWidget 30 | from PySide2.QtWidgets import QApplication 31 | import os 32 | import logging 33 | 34 | from typing import TYPE_CHECKING 35 | if TYPE_CHECKING: 36 | from controller.controller import Controller 37 | else: 38 | from typing import Any as Controller 39 | 40 | 41 | class ViewOptions(QWidget): 42 | 43 | """ 44 | ViewOptions 45 | Handles options of EMCA 46 | """ 47 | 48 | def __init__(self, parent=None): 49 | QWidget.__init__(self, parent=None) 50 | ui_filepath = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'ui', 'options.ui')) 51 | loadUi(ui_filepath, self) 52 | 53 | self._controller = None 54 | 55 | # center widget depending on screen size 56 | desktop_widget = QApplication.desktop() 57 | screen_rect = desktop_widget.availableGeometry(self) 58 | self.move(screen_rect.center() - self.rect().center()) 59 | 60 | self._theme_buttons = QButtonGroup() 61 | self._theme_buttons.addButton(self.cbDarkTheme) 62 | self._theme_buttons.addButton(self.cbLightTheme) 63 | 64 | self.btnSave.clicked.connect(self.btn_save) 65 | self.btnClose.clicked.connect(self.btn_close) 66 | 67 | def set_controller(self, controller : Controller): 68 | """ 69 | Set the connection to the controller 70 | :param controller: Controller 71 | :return: 72 | """ 73 | self._controller = controller 74 | 75 | def enable_dark_theme(self, enabled): 76 | self.cbDarkTheme.setChecked(enabled) 77 | 78 | def enable_light_theme(self, enabled): 79 | self.cbLightTheme.setChecked(enabled) 80 | 81 | def get_theme(self): 82 | theme = 'dark' 83 | if self.cbDarkTheme.isChecked(): 84 | theme = 'dark' 85 | elif self.cbLightTheme.isChecked(): 86 | theme = 'light' 87 | return theme 88 | 89 | def get_auto_connect(self): 90 | return self.cbAutoConnect.isChecked() 91 | 92 | def get_auto_scene_load(self): 93 | return self.cbAutoSceneLoad.isChecked() 94 | 95 | def get_auto_image_load(self): 96 | return self.cbAutoLoadImage.isChecked() 97 | 98 | def set_auto_connect(self, enabled): 99 | self.cbAutoConnect.setChecked(enabled) 100 | 101 | def set_auto_scene_load(self, enabled): 102 | self.cbAutoSceneLoad.setChecked(enabled) 103 | 104 | def set_auto_image_load(self, enabled): 105 | self.cbAutoLoadImage.setChecked(enabled) 106 | 107 | @Slot(bool, name='btn_save') 108 | def btn_save(self, clicked): 109 | options_dict = {'theme': self.get_theme(), 110 | 'auto_connect': self.get_auto_connect(), 111 | 'auto_scene_load': self.get_auto_scene_load(), 112 | 'auto_rendered_image_load': self.get_auto_image_load()} 113 | self._controller.options.save_options(options_dict) 114 | 115 | @Slot(bool, name='btn_close') 116 | def btn_close(self, clicked): 117 | self.close() 118 | -------------------------------------------------------------------------------- /view/view_main/view_render_settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from PySide2.QtGui import QKeyEvent 27 | from model.render_info import RenderInfo 28 | from core.pyside2_uic import loadUi 29 | from PySide2.QtWidgets import QWidget 30 | from PySide2.QtWidgets import QApplication 31 | from PySide2.QtCore import Slot 32 | from PySide2.QtCore import Qt 33 | import os 34 | 35 | from typing import TYPE_CHECKING 36 | if TYPE_CHECKING: 37 | from controller.controller import Controller 38 | else: 39 | from typing import Any as Controller 40 | 41 | 42 | class ViewRenderSettings(QWidget): 43 | 44 | """ 45 | ViewRenderSettings 46 | Handles general information about the scene 47 | """ 48 | 49 | def __init__(self, parent=None): 50 | QWidget.__init__(self, parent=None) 51 | ui_filepath = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'ui', 'render_info.ui')) 52 | loadUi(ui_filepath, self) 53 | 54 | self._controller = None 55 | 56 | # center widget depending on screen size 57 | desktop_widget = QApplication.desktop() 58 | screen_rect = desktop_widget.availableGeometry(self) 59 | self.move(screen_rect.center() - self.rect().center()) 60 | 61 | self.btnClose.clicked.connect(self.close) 62 | 63 | def set_controller(self, controller : Controller): 64 | """ 65 | Sets the connection to the controller 66 | :param controller: Controller 67 | :return: 68 | """ 69 | self._controller = controller 70 | self.sbSampleCount.valueChanged.connect(self.sb_update_sample_count) 71 | 72 | def update_render_info(self, render_info : RenderInfo): 73 | """ 74 | Updates the view info with new render information 75 | :param render_info: RenderInfo from model 76 | :return: 77 | """ 78 | self.labelRendererName.setText(render_info.renderer_name) 79 | self.labelSceneName.setText(render_info.scene_name) 80 | self.sbSampleCount.setValue(render_info.sample_count) 81 | 82 | def keyPressEvent(self, event : QKeyEvent): 83 | """ 84 | Handles key press event, if enter is pressed the submit button will be triggered 85 | """ 86 | if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: 87 | self.btn_submit(True) 88 | 89 | @Slot(int, name='sb_update_sample_count') 90 | def sb_update_sample_count(self, value : int): 91 | """ 92 | Informs the controller to update the sample count of render info 93 | """ 94 | self._controller.update_render_info_sample_count(value) 95 | -------------------------------------------------------------------------------- /view/view_pixel_data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/view/view_pixel_data/__init__.py -------------------------------------------------------------------------------- /view/view_render_image/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/view/view_render_image/__init__.py -------------------------------------------------------------------------------- /view/view_render_image/hdr_graphics_view.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.hdr_graphics_view_base import HDRGraphicsViewBase 27 | from PySide2.QtCore import QPoint 28 | import logging 29 | 30 | 31 | from typing import TYPE_CHECKING 32 | if TYPE_CHECKING: 33 | from view.view_render_image.view_render_image import ViewRenderImage 34 | else: 35 | from typing import Any as ViewRenderImage 36 | 37 | 38 | class HDRGraphicsView(HDRGraphicsViewBase): 39 | 40 | """ 41 | HDRGraphicsView 42 | Custom QGraphicsView which holds the rendered image and handles interactions 43 | """ 44 | 45 | def __init__(self, parent : ViewRenderImage): 46 | HDRGraphicsViewBase.__init__(self) 47 | self._parent = parent 48 | self._old_scene_pos = QPoint() 49 | 50 | def mousePressEvent(self, q_mouse_event): 51 | """ 52 | Handles a mouse press event, aves the current position. 53 | A request to the controller will only be send if the position will be the same after mouse btn release. 54 | :param q_mouse_event: 55 | :return: 56 | """ 57 | global_pos = q_mouse_event.globalPos() 58 | self._old_scene_pos = self.transform_to_scene_pos(global_pos) 59 | super().mousePressEvent(q_mouse_event) 60 | 61 | def mouseReleaseEvent(self, q_mouse_event): 62 | """ 63 | Handles a mouse button release. 64 | Informs the controller if the position has not changed since the click. 65 | Otherwise the image will be moved. 66 | :param q_mouse_event: 67 | :return: 68 | """ 69 | global_pos = q_mouse_event.globalPos() 70 | new_pos = self.transform_to_scene_pos(global_pos) 71 | if self._old_scene_pos == new_pos: 72 | pixel = self.transform_to_image_coordinate(q_mouse_event.globalPos()) 73 | if self.pixel_within_bounds(pixel): 74 | self._parent.request_pixel_data(pixel) 75 | super().mouseReleaseEvent(q_mouse_event) 76 | 77 | def mouseMoveEvent(self, q_mouse_event): 78 | """ 79 | Handles mouse move event 80 | :param q_mouse_event: 81 | :return: 82 | """ 83 | image_coord = self.transform_to_image_coordinate(q_mouse_event.globalPos()) 84 | text = '({},{})'.format(image_coord.x(), image_coord.y()) 85 | self._parent.labelCurrentPos.setText(text) 86 | super().mouseMoveEvent(q_mouse_event) 87 | 88 | def dropEvent(self, q_drop_event): 89 | try: 90 | super().dropEvent(q_drop_event) 91 | self._parent.enable_view(True) 92 | self._parent.save_last_rendered_image_filepath() 93 | except Exception as e: 94 | logging.error(e) 95 | 96 | -------------------------------------------------------------------------------- /view/view_render_scene/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/view/view_render_scene/__init__.py -------------------------------------------------------------------------------- /view/view_render_scene/view_render_scene.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | import typing 27 | import numpy as np 28 | from model.pixel_data import PixelData 29 | from renderer.scene_renderer import SceneRenderer 30 | from PySide2.QtWidgets import QWidget 31 | from core.pyside2_uic import loadUi 32 | import os 33 | import logging 34 | 35 | from typing import TYPE_CHECKING 36 | if TYPE_CHECKING: 37 | from controller.controller import Controller 38 | else: 39 | from typing import Any as Controller 40 | 41 | 42 | class ViewRenderScene(QWidget): 43 | 44 | """ 45 | ViewRenderScene 46 | Handles the three-dimensional visualization. 47 | """ 48 | 49 | def __init__(self, parent=None): 50 | QWidget.__init__(self, parent=parent) 51 | ui_filepath = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'ui', 'render_scene.ui')) 52 | loadUi(ui_filepath, self) 53 | 54 | self._controller = None 55 | self._scene_renderer = None 56 | 57 | def set_controller(self, controller : Controller): 58 | """ 59 | Sets the connection to the controller 60 | """ 61 | self._controller = controller 62 | self.btnSceneOptions.clicked.connect(controller.display_view_render_scene_options) 63 | self.btnLoadScene.clicked.connect(controller.stream.request_scene_data) 64 | self.btnReset.clicked.connect(controller.scene.reset_camera_position) 65 | 66 | def init_scene_renderer(self, scene_renderer : SceneRenderer): 67 | """ 68 | Initializes the scene renderer in this view render scene and view render scene options 69 | """ 70 | self._scene_renderer = scene_renderer 71 | self.sceneLayout.addWidget(scene_renderer.renderer.widget) 72 | 73 | @property 74 | def scene_renderer(self) -> SceneRenderer: 75 | """ 76 | Returns the SceneRender object 77 | """ 78 | return self._scene_renderer 79 | 80 | def enable_view(self, enabled : bool): 81 | """ 82 | Enables the view elements 83 | """ 84 | self.btnLoadScene.setEnabled(enabled) 85 | if enabled: # do not disable these once enabled 86 | self.btnReset.setEnabled(enabled) 87 | self.btnSceneOptions.setEnabled(enabled) 88 | 89 | def load_traced_paths(self, pixel_data : PixelData): 90 | """ 91 | Informs the renderer to visualize the traced paths 92 | """ 93 | self._scene_renderer.load_traced_paths(pixel_data) 94 | 95 | def clear_traced_paths(self): 96 | """ 97 | Informs the renderer to clear all visualizes traced paths 98 | """ 99 | self._scene_renderer.clear_traced_paths() 100 | 101 | def update_path_indices(self, indices : np.ndarray): 102 | """ 103 | Updates the view with given path indices keys from controller 104 | """ 105 | self._scene_renderer.update_path_indices(indices) 106 | 107 | def select_path(self, path_idx : typing.Optional[int]): 108 | """ 109 | Informs the renderer to visualize the path with index: index 110 | """ 111 | self._scene_renderer.select_path(path_idx) 112 | 113 | def select_intersection(self, path_idx : typing.Optional[int], its_idx : typing.Optional[int]): 114 | """ 115 | Informs the renderer to select / highlight the intersection with tuple tpl 116 | """ 117 | self._scene_renderer.select_intersection(path_idx, its_idx) 118 | -------------------------------------------------------------------------------- /view/view_sample_contribution/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtuebingen/emca/560975bddc1b6176fe25029acb13d7806c8ab35b/view/view_sample_contribution/__init__.py -------------------------------------------------------------------------------- /view/view_sample_contribution/view_sample_contribution_plot.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.plot_rgb import RGBScatterPlot 27 | from PySide2.QtWidgets import QWidget 28 | from PySide2.QtWidgets import QVBoxLayout 29 | import logging 30 | 31 | from model.contribution_data import SampleContributionData 32 | 33 | from typing import TYPE_CHECKING 34 | if TYPE_CHECKING: 35 | from controller.controller import Controller 36 | else: 37 | from typing import Any as Controller 38 | 39 | 40 | class ViewSampleContribution(QWidget): 41 | 42 | """ 43 | ViewSampleContribution 44 | Handles the plot view of the final estimate data per path. 45 | A matplotlib plot is embedded within a QWidget for visualization 46 | """ 47 | 48 | def __init__(self, parent=None): 49 | QWidget.__init__(self, parent=parent) 50 | 51 | self._controller = None 52 | # matplotlib plot embedded within a QWidget 53 | self._sample_contribution_plot = RGBScatterPlot(callback=self.send_update_path) 54 | 55 | self._visible = True 56 | self._data = None 57 | 58 | # add matplotlib navigation toolbar 59 | layout = QVBoxLayout(self) 60 | layout.addWidget(self._sample_contribution_plot) 61 | layout.addWidget(self._sample_contribution_plot.create_navigation_toolbar(self)) 62 | 63 | def set_controller(self, controller : Controller): 64 | """ 65 | Sets the connection to the controller 66 | """ 67 | self._controller = controller 68 | 69 | def apply_theme(self, theme): 70 | self._sample_contribution_plot.apply_theme(theme) 71 | 72 | def plot_final_estimate(self, final_estimate : SampleContributionData): 73 | """ 74 | Plot SampleContribution data with final estimate values from model 75 | """ 76 | if not self._visible: 77 | self._data = final_estimate 78 | return 79 | 80 | self._sample_contribution_plot.plot_rgb(final_estimate.indices, 81 | final_estimate.red, 82 | final_estimate.green, 83 | final_estimate.blue) 84 | 85 | @property 86 | def visible(self): 87 | return self._visible 88 | 89 | @visible.setter 90 | def visible(self, visible): 91 | self._visible = visible 92 | if self._visible and self._data is not None: 93 | self.plot_final_estimate(self._data) 94 | self._data = None 95 | 96 | def update_path_indices(self, indices): 97 | """ 98 | Inform the plot to mark the given indices 99 | :param indices: numpy array of path indices 100 | :return: 101 | """ 102 | self.visible = True 103 | self._sample_contribution_plot.highlight(indices) 104 | 105 | def send_update_path(self, indices, add_index): 106 | """ 107 | Inform the controller about selected points from the plot highlighter, 108 | depending on add_index the indices are added to the current selected ones 109 | :param indices: numpy array of path indices 110 | :param add_index: boolean 111 | :return: 112 | """ 113 | self._controller.update_path(indices, add_index) 114 | 115 | -------------------------------------------------------------------------------- /view/view_sample_contribution/view_sample_depth_plot.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.plot_2d import ScatterPlot2D 27 | from PySide2.QtWidgets import QWidget 28 | from PySide2.QtWidgets import QVBoxLayout 29 | import logging 30 | 31 | from model.contribution_data import SampleContributionData 32 | 33 | from typing import TYPE_CHECKING 34 | if TYPE_CHECKING: 35 | from controller.controller import Controller 36 | else: 37 | from typing import Any as Controller 38 | 39 | 40 | class ViewSampleDepth(QWidget): 41 | 42 | """ 43 | ViewSampleContribution 44 | Handles the plot view of the final estimate data per path. 45 | A matplotlib plot is embedded within a QWidget for visualization 46 | """ 47 | 48 | def __init__(self, parent=None): 49 | QWidget.__init__(self, parent=parent) 50 | 51 | self._controller = None 52 | # matplotlib plot embedded within a QWidget 53 | self._sample_contribution_plot = ScatterPlot2D(callback=self.send_update_path, title='Depth') 54 | 55 | self._visible = True 56 | self._data = None 57 | 58 | # add matplotlib navigation toolbar 59 | layout = QVBoxLayout(self) 60 | layout.addWidget(self._sample_contribution_plot) 61 | layout.addWidget(self._sample_contribution_plot.create_navigation_toolbar(self)) 62 | 63 | def set_controller(self, controller : Controller): 64 | """ 65 | Sets the connection to the controller 66 | """ 67 | self._controller = controller 68 | 69 | def apply_theme(self, theme): 70 | self._sample_contribution_plot.apply_theme(theme) 71 | 72 | def plot_final_estimate(self, final_estimate : SampleContributionData): 73 | """ 74 | Plot SampleContribution data with final estimate values from model 75 | """ 76 | if not self._visible: 77 | self._data = final_estimate 78 | return 79 | 80 | self._sample_contribution_plot.plot_2d(final_estimate.indices, 81 | final_estimate.depth) 82 | 83 | @property 84 | def visible(self): 85 | return self._visible 86 | 87 | @visible.setter 88 | def visible(self, visible): 89 | self._visible = visible 90 | if self._visible and self._data is not None: 91 | self.plot_final_estimate(self._data) 92 | self._data = None 93 | 94 | def update_path_indices(self, indices): 95 | """ 96 | Inform the plot to mark the given indices 97 | :param indices: numpy array of path indices 98 | :return: 99 | """ 100 | self.visible = True 101 | self._sample_contribution_plot.highlight(indices) 102 | 103 | def send_update_path(self, indices, add_index): 104 | """ 105 | Inform the controller about selected points from the plot highlighter, 106 | depending on add_index the indices are added to the current selected ones 107 | :param indices: numpy array of path indices 108 | :param add_index: boolean 109 | :return: 110 | """ 111 | self._controller.update_path(indices, add_index) 112 | 113 | -------------------------------------------------------------------------------- /view/view_sample_contribution/view_sample_lum_plot.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 Christoph Kreisl 5 | Copyright (c) 2021 Lukas Ruppert 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from core.plot_2d import ScatterPlot2D 27 | from PySide2.QtWidgets import QWidget 28 | from PySide2.QtWidgets import QVBoxLayout 29 | import logging 30 | 31 | from model.contribution_data import SampleContributionData 32 | 33 | from typing import TYPE_CHECKING 34 | if TYPE_CHECKING: 35 | from controller.controller import Controller 36 | else: 37 | from typing import Any as Controller 38 | 39 | 40 | class ViewSampleLuminance(QWidget): 41 | 42 | """ 43 | ViewSampleContribution 44 | Handles the plot view of the final estimate data per path. 45 | A matplotlib plot is embedded within a QWidget for visualization 46 | """ 47 | 48 | def __init__(self, parent=None): 49 | QWidget.__init__(self, parent=parent) 50 | 51 | self._controller = None 52 | # matplotlib plot embedded within a QWidget 53 | self._sample_contribution_plot = ScatterPlot2D(callback=self.send_update_path, title='Luminance') 54 | 55 | self._visible = True 56 | self._data = None 57 | 58 | # add matplotlib navigation toolbar 59 | layout = QVBoxLayout(self) 60 | layout.addWidget(self._sample_contribution_plot) 61 | layout.addWidget(self._sample_contribution_plot.create_navigation_toolbar(self)) 62 | 63 | def set_controller(self, controller : Controller): 64 | """ 65 | Sets the connection to the controller 66 | """ 67 | self._controller = controller 68 | 69 | def apply_theme(self, theme): 70 | self._sample_contribution_plot.apply_theme(theme) 71 | 72 | def plot_final_estimate(self, final_estimate : SampleContributionData): 73 | """ 74 | Plot SampleContribution data with final estimate values from model 75 | """ 76 | if not self._visible: 77 | self._data = final_estimate 78 | return 79 | 80 | self._sample_contribution_plot.plot_2d(final_estimate.indices, 81 | final_estimate.luminance) 82 | 83 | @property 84 | def visible(self): 85 | return self._visible 86 | 87 | @visible.setter 88 | def visible(self, visible): 89 | self._visible = visible 90 | if self._visible and self._data is not None: 91 | self.plot_final_estimate(self._data) 92 | self._data = None 93 | 94 | def update_path_indices(self, indices): 95 | """ 96 | Inform the plot to mark the given indices 97 | :param indices: numpy array of path indices 98 | :return: 99 | """ 100 | self.visible = True 101 | self._sample_contribution_plot.highlight(indices) 102 | 103 | def send_update_path(self, indices, add_index): 104 | """ 105 | Inform the controller about selected points from the plot highlighter, 106 | depending on add_index the indices are added to the current selected ones 107 | :param indices: numpy array of path indices 108 | :param add_index: boolean 109 | :return: 110 | """ 111 | self._controller.update_path(indices, add_index) 112 | 113 | --------------------------------------------------------------------------------