Beispiel #1
0
class WindowGL(QWindow):
    def __init__(self):
        super().__init__()

        fmt = QSurfaceFormat()
        fmt.setVersion(2, 0)
        fmt.setProfile(QSurfaceFormat.CompatibilityProfile)
        fmt.setStereo(False)
        fmt.setSwapBehavior(QSurfaceFormat.DoubleBuffer)

        self.setSurfaceType(QWindow.OpenGLSurface)
        self.context = QOpenGLContext()
        self.context.setFormat(fmt)

        if not self.context.create():
            raise Exception("Unable to create context")

        self.create()
        self.setTitle("OpenGL")

    def exposeEvent(self, e):
        e.accept()
        if self.isExposed() and self.isVisible():
            self.update()

    def makeCurrent(self):
        self.context.makeCurrent(self)

    def swapBuffers(self):
        self.context.swapBuffers(self)

    def resize(self, w, h):
        super().resize(w, h)
        self.makeCurrent()
        GL.glViewport(0, 0, w, h)

    def update(self):
        tri_size = 0.7
        self.makeCurrent()
        GL.glClearColor(0, 0, 0, 0)
        GL.glClearDepth(1)
        GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
        GL.glBegin(GL.GL_TRIANGLES)
        GL.glVertex2f(0, tri_size)
        GL.glColor3f(1, 0, 0)
        GL.glVertex2f(-tri_size, -tri_size)
        GL.glColor3f(0, 1, 0)
        GL.glVertex2f(tri_size, -tri_size)
        GL.glColor3f(0, 0, 1)
        GL.glEnd()
        GL.glFlush()
        self.swapBuffers()
Beispiel #2
0
class OpenGLWindow(QWindow):
    def __init__(self, parent=None):
        super(OpenGLWindow, self).__init__(parent)

        self.m_update_pending = False
        self.m_animating = False
        self.m_context = None
        self.m_gl = None

        self.setSurfaceType(QWindow.OpenGLSurface)

    def initialize(self):
        pass

    def setAnimating(self, animating):
        self.m_animating = animating

        if animating:
            self.renderLater()

    def renderLater(self):
        if not self.m_update_pending:
            self.m_update_pending = True
            QGuiApplication.postEvent(self, QEvent(QEvent.UpdateRequest))

    def renderNow(self):
        if not self.isExposed():
            return

        self.m_update_pending = False

        needsInitialize = False

        if self.m_context is None:
            self.m_context = QOpenGLContext(self)
            self.m_context.setFormat(self.requestedFormat())
            self.m_context.create()

            needsInitialize = True

        self.m_context.makeCurrent(self)

        if needsInitialize:
            version_profile = QOpenGLVersionProfile()
            version_profile.setVersion(2, 0)
            self.m_gl = self.m_context.versionFunctions(version_profile)
            self.m_gl.initializeOpenGLFunctions()

            self.initialize()

        self.render(self.m_gl)

        self.m_context.swapBuffers(self)

        if self.m_animating:
            self.renderLater()

    def event(self, event):
        if event.type() == QEvent.UpdateRequest:
            self.renderNow()
            return True

        return super(OpenGLWindow, self).event(event)

    def exposeEvent(self, event):
        self.renderNow()

    def resizeEvent(self, event):
        self.renderNow()
Beispiel #3
0
class Window(QWindow):
    """
    A QWindow for rendering of WindowProxy

    Attributes
    ----------
    _context : QOpenGLContext
        Context used to render the window contents
    _window_proxy_id : int
        The WindowProxy responsible for drawing
    _saved_size : QSize
        If a resize event occurs before WindowProxy is started, then the size is saved here so that it can be set when
        WindowProxy is started
    _proxy_started_once : bool
        True after the first time that _of has been started

    """
    def __init__(self, nrow=1, ncol=1, id=0):
        """
        Create an instance of OFWindow

        Attributes
        ----------
        nrow : int, optional
            Number of rows in WindowProxy grid, default = 1
        ncol : int, optional
            Number of columns in WindowProxy grid, default = 1
        id : int, optional
            Identifier for the WindowProxy (each WindowProxy should have a unique identifier), default = 0

        """
        super().__init__()
        self._context = None
        self._proxy_started = False
        self._saved_size = None
        self.setSurfaceType(QWindow.OpenGLSurface)

        self._window_proxy_id = id

        ofwin_createproxy(0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT, nrow, ncol,
                          True, self._window_proxy_id, False)
        ofwin_setmakecurrentfunction(self.make_current)
        ofwin_setupdatecontextfunction(self.make_current)
        ofwin_setswapbuffersfunction(self.swap_buffers)

    def exposeEvent(self, event):
        """
        Overrides QWindow.exposeEvent()

        """
        if not self._proxy_started:
            self._proxy_started = True
            ofwin_activate(self._window_proxy_id)
            ofwin_start()
            if self._saved_size is not None:
                ofwin_resizewindow(0, 0, self._saved_size.width(),
                                   self._saved_size.height())
                self._saved_size = None

    def hideEvent(self, event):
        """
        Overrides QWindow.exposeEvent()

        """
        ofwin_activate(self._window_proxy_id)
        ofwin_signalstop()
        while ofwin_isrunning() == 1:
            QCoreApplication.processEvents(QEventLoop.AllEvents, 100)
        self._proxy_started = False

    def resizeEvent(self, event):
        """
        Overrides QWindow.resizeEvent()

        """
        ofwin_activate(self._window_proxy_id)
        if ofwin_isrunning() == 1:
            ofwin_resizewindow(0, 0,
                               event.size().width(),
                               event.size().height())
        else:
            self._saved_size = event.size()

    def mousePressEvent(self, event):
        """
        Overrides QWindow.mousePressEvent()

        """
        ofwin_activate(self._window_proxy_id)
        if ofwin_isrunning() == 1:
            button = Window._map_qt_button_to_of_button(event.button())
            if button != 0:
                ofwin_buttonpress(event.x(), event.y(), button)

    def mouseReleaseEvent(self, event):
        """
        Overrides QWindow.mouseReleaseEvent()

        """
        ofwin_activate(self._window_proxy_id)
        if ofwin_isrunning() == 1:
            button = Window._map_qt_button_to_of_button(event.button())
            if button != 0:
                ofwin_buttonrelease(event.x(), event.y(), button)

    def mouseMoveEvent(self, event):
        """
        Overrides QWindow.mouseMoveEvent()

        """
        ofwin_activate(self._window_proxy_id)
        if ofwin_isrunning() == 1:
            ofwin_mousemotion(event.x(), event.y())

    def keyPressEvent(self, event):
        """
        Overrides QWindow.keyPressEvent()

        """
        ofwin_activate(self._window_proxy_id)
        if ofwin_isrunning() == 1:
            key = Window._map_qt_key_event_to_osg_key(event)
            ofwin_keypress(key)

    # TODO call glGetError() to print any errors that may have occurred
    def make_current(self):
        """
        Makes _context current for the surface of this window

        Returns
        -------
        bool
            True if successful
            False if an error occurs

        """
        success = False
        if self._context is None:
            self._context = QOpenGLContext()
            self._context.create()
            success = self._context.makeCurrent(self)
            if success:
                # self.initializeOpenGLFunctions()
                self._context.doneCurrent()
            else:
                return success
        if self._context is not None:
            success = self._context.makeCurrent(self)
            # err = glGetError()
        return success

    def swap_buffers(self):
        """
        Swaps the buffer from _context to the surface of this window

        """
        if self._context is not None:
            self._context.swapBuffers(self)

    @staticmethod
    def _map_qt_button_to_of_button(qt_button):
        """
        Maps a Qt.MouseButton enumeration to an int for OpenFrames

        Parameters
        ----------
        qt_button : Qt.MouseButton
            The button to map

        Returns
        -------
        int
            The corresponding button for OpenFrames

        """
        if qt_button == Qt.LeftButton:
            return 1
        elif qt_button == Qt.RightButton:
            return 3
        elif qt_button == Qt.MiddleButton:
            return 2
        elif qt_button == Qt.BackButton:
            return 6
        elif qt_button == Qt.ForwardButton:
            return 7
        else:
            return 0

    @staticmethod
    def _map_qt_key_event_to_osg_key(event):
        """
        Maps a QKeyEvent to an int for OpenFrames

        Parameters
        ----------
        event : PyQt5.QtGui.QKeyEvent.QKeyEvent
            The key event to map

        Returns
        -------
        int
            The corresponding key code for OpenFrames

        """
        if Qt.Key_A <= event.key() <= Qt.Key_Z:
            if event.modifiers() & Qt.ShiftModifier:
                key = event.key()
            else:
                key = event.key() + 0x20
        else:
            key = event.key()
        return key
Beispiel #4
0
class OpenGLWindow(QWindow):
    def __init__(self, parent=None):
        super(OpenGLWindow, self).__init__(parent)

        self.m_update_pending = False
        self.m_animating = False
        self.m_context = None
        self.m_gl = None

        self.setSurfaceType(QWindow.OpenGLSurface)

    def initialize(self):
        pass

    def setAnimating(self, animating):
        self.m_animating = animating

        if animating:
            self.renderLater()

    def renderLater(self):
        if not self.m_update_pending:
            self.m_update_pending = True
            QGuiApplication.postEvent(self, QEvent(QEvent.UpdateRequest))

    def renderNow(self):
        if not self.isExposed():
            return

        self.m_update_pending = False

        needsInitialize = False

        if self.m_context is None:
            self.m_context = QOpenGLContext(self)
            self.m_context.setFormat(self.requestedFormat())
            self.m_context.create()

            needsInitialize = True

        self.m_context.makeCurrent(self)

        if needsInitialize:
            self.m_gl = self.m_context.versionFunctions()
            self.m_gl.initializeOpenGLFunctions()

            self.initialize()

        self.render(self.m_gl)

        self.m_context.swapBuffers(self)

        if self.m_animating:
            self.renderLater()

    def event(self, event):
        if event.type() == QEvent.UpdateRequest:
            self.renderNow()
            return True

        return super(OpenGLWindow, self).event(event)

    def exposeEvent(self, event):
        self.renderNow()

    def resizeEvent(self, event):
        self.renderNow()
class OpenGLWindow(QWindow):
    def __init__(self, parent=None):
        super(OpenGLWindow, self).__init__(parent)

        self.m_update_pending = False
        self.m_animating = False
        self.m_context = None
        self.m_device = None
        self.m_gl = None
        self.logger = None

        self.setSurfaceType(QWindow.OpenGLSurface)

    def initialize(self, gl):
        pass

    def setAnimating(self, animating):
        self.m_animating = animating

        if animating:
            self.renderLater()

    def renderLater(self):
        if not self.m_update_pending:
            self.m_update_pending = True
            QGuiApplication.postEvent(self, QEvent(QEvent.UpdateRequest))

    def paint(self, painter):
        pass

    def render(self, gl):
        pass

    def addGlFunctuins(self, GL, functions):
        for function, arguments in functions.items():
            GL[function].restype = None
            GL[function].argtypes = arguments
            setattr(self.m_gl, function, GL[function])

    @exitOnKeyboardInterrupt
    def renderNow(self):
        if not self.isExposed():
            return

        self.m_update_pending = False

        needsInitialize = False

        if self.m_context is None:
            self.m_context = QOpenGLContext(self)
            self.m_context.setFormat(self.requestedFormat())
            self.m_context.create()

            needsInitialize = True

        self.m_context.makeCurrent(self)

        if needsInitialize:
#           Sorry, no support for higher versions for now.
            profile = QOpenGLVersionProfile()
            profile.setVersion(2, 0)

            self.m_gl = self.m_context.versionFunctions(profile)
            self.m_gl.initializeOpenGLFunctions()

            #print(self.m_context.hasExtension('GL_EXT_framebuffer_object'))
            #print(self.m_context.hasExtension('GL_ARB_texture_float'))
            #print(*sorted(self.m_context.extensions()), sep='\n')

#           Small hack. Guess noone mind?            
            import ctypes
            import ctypes.util
            GL = ctypes.CDLL(ctypes.util.find_library('GL'))

            self.addGlFunctuins(GL, {
                'glFramebufferTexture2D': (ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_int)
                })

            self.logger = QOpenGLDebugLogger()
            self.logger.initialize()
            self.logger.loggedMessages()
            self.logger.messageLogged.connect(self.handleLoggedMassage)
            self.logger.startLogging()

            self.initialize(self.m_gl)
        
        if not self.m_device:
            self.m_device = QOpenGLPaintDevice()

        self.m_gl.glClear(self.m_gl.GL_COLOR_BUFFER_BIT | self.m_gl.GL_DEPTH_BUFFER_BIT);

        self.m_device.setSize(self.size())

        painter = QPainter(self.m_device)


        painter.beginNativePainting()
        self.render(self.m_gl)
        painter.endNativePainting()

        self.paint(painter)

        self.m_context.swapBuffers(self)

        if self.m_animating:
            self.renderLater()

    def handleLoggedMassage(self, message):
#       This three really annoyng and brings no useful info =\        
        if not (message.message().find('Use glDrawRangeElements() to avoid this.') > -1 or 
                message.message().find('CPU mapping a busy miptree') > -1 or
                message.message().find('Flushing before mapping a referenced bo.') > -1
                ):
            print(message.message().strip())

    def event(self, event):
        if event.type() == QEvent.UpdateRequest:
            self.renderNow()
            return True

        return super(OpenGLWindow, self).event(event)

    def exposeEvent(self, event):
        self.renderNow()

    def resizeEvent(self, event):
        self.renderNow()
Beispiel #6
0
class ViewerWindow(QWindow):

    instructions = """
--Key controls
0-9     - toggle data layers
r       - reset view parameters
c       - clip view
t       - view from top
l       - light/dark background
=       - increase point size
-       - decrease point size

--Mouse controls
drag               
shift + move         - translate dataset
scroll               - scale dataset
shift + scroll       - change field of view
ctrl + scroll        - move far clipping plane
alt + scroll         - move near clipping plane
ctrl + alt + scroll  - move far and near clipping plane simultaniously
"""

    def __init__(self, parent=None, **kwargs):
        super(ViewerWindow, self).__init__(parent)
        self.setSurfaceType(QWindow.OpenGLSurface)

        format = QSurfaceFormat()
        format.setVersion(3, 3)
        format.setProfile(QSurfaceFormat.CoreProfile)
        format.setStereo(False)
        format.setSwapBehavior(QSurfaceFormat.DoubleBuffer)
        format.setDepthBufferSize(24)
        format.setSamples(16)
        
        self.context = QOpenGLContext(self)
        self.context.setFormat(format)
        if not self.context.create():
            raise Exception('self.context.create() failed')
        self.create()

        size = 720, 720
        self.resize(*size)

        self.context.makeCurrent(self)
        self.hud_program = CrossHairProgram()

        self.default_view = np.eye(4, dtype=np.float32)
        self.view = self.default_view
        self.model = np.eye(4, dtype=np.float32)
        self.projection = np.eye(4, dtype=np.float32)

        self.layer_manager = LayerManager()

        self.visibility_toggle_listeners = []
        self.multiview = True

        self.rotation = q.quaternion()
        self.scale = 0.6
        self.translation = np.zeros(3)

        self.radius = 0.5 * min(*size)
        self.fov = 5.
        self.camera_position = -12.
        self.near_clip = .1
        if 'near_clip' in kwargs:
            self.near_clip = kwargs['near_clip']
        self.far_clip = 100.
        if 'far_clip' in kwargs:
            self.far_clip = kwargs['far_clip']
        

        self.projection_mode = 'perspective' # 'orthographic'

        self.size = size
        self.bg_white = False
        self.viewpoint_dict = {}

        print((self.instructions))

    def add_layer(self, layer):
        self.layer_manager.add_layer(layer)
        return layer

    # keeping this compatibility with older scripts 
    def add_data_source(self, name, opts, points, normals=None, radii=None, intensity=None, category=None, zrange=None, **kwargs):
        if len(self.layer_manager.layers) == 0:
            self.layer_manager.add_layer(Layer(name='Default'))
        self.layer_manager.layers['Default'].add_data_source(name, opts, points, normals=normals, radii=radii, intensity=intensity, category=category, zrange=zrange, **kwargs)

    def add_data_source_line(self, name, coords_start, coords_end, **args):
        if len(self.layer_manager.layers) == 0:
            self.layer_manager.add_layer(Layer(name='Default'))
        self.layer_manager.layers['Default'].add_data_source_line(name, coords_start, coords_end, **args)

    def add_data_source_triangle(self, name, coords, normals, **args):
        if len(self.layer_manager.layers) == 0:
            self.layer_manager.add_layer(Layer(name='Default'))
        self.layer_manager.layers['Default'].add_data_source_triangle(name, coords, normals, **args)

    def run(self):
        self.initialize()
        self.show()

    def set_layer_visibility(self, name, is_visible):
        for listener in self.visibility_toggle_listeners:
            listener(name, is_visible)

    def center_view(self, center=None):
        if center is None:
            center = self.layer_manager.first.get_center()
            data_range = self.layer_manager.first.data_range
            self.data_width = data_range[0]
            self.data_height = data_range[2]
        self.translation = np.zeros(3)
        self.model = np.eye(4, dtype=np.float32)
        translate(self.model, -center[0], -center[1], -center[2])
        for program in self.layer_manager.programs():
            program.setUniform('u_model', self.model)
        self.update_view_matrix()

    def initialize(self):
        self.context.makeCurrent(self)

        gl.glDepthMask(gl.GL_TRUE)
        gl.glEnable(gl.GL_DEPTH_TEST)
        gl.glDepthFunc(gl.GL_LESS)

        self.set_bg()

        view_width, view_height = [x/self.radius for x in self.size]

        self.center_view()
        for program in self.layer_manager.programs():
            program.setUniform('u_model', self.model)
            program.setUniform('u_view', self.view)
            program.setUniform('u_projection', self.projection)

        self.modelscale = .6* 2*min(2.*view_width/self.data_width, 2.*view_height/self.data_height)
        self.scale = self.modelscale
        self.update_view_matrix()

        self.last_mouse_pos = 0,0

        self.on_resize(*self.size)

    def render(self):
        if not self.isExposed():
            return
        self.context.makeCurrent(self)

        bits = 0
        bits |= gl.GL_COLOR_BUFFER_BIT
        bits |= gl.GL_DEPTH_BUFFER_BIT
        bits |= gl.GL_STENCIL_BUFFER_BIT
        gl.glClear(bits)
        gl.glEnable(gl.GL_PROGRAM_POINT_SIZE)
        
        for program in self.layer_manager.programs():
            if program.do_blending:
                if self.bg_white:
                    gl.glEnable(gl.GL_BLEND)
                    gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_SRC_ALPHA)
                else:
                    gl.glEnable(gl.GL_BLEND)
                    gl.glBlendFunc(gl.GL_ONE, gl.GL_SRC_ALPHA)
            else:
                gl.glDisable(gl.GL_BLEND)
        self.layer_manager.draw()
        
        if self.hud_program.is_visible:
            self.hud_program.draw()

        self.context.swapBuffers(self)

    def event(self, event):
        # print event.type()
        if event.type() == QEvent.UpdateRequest:
            self.render()
            return True

        return super(ViewerWindow, self).event(event)

    def exposeEvent(self, event):
        self.render()

    def resizeEvent(self, event):
        size = event.size()
        self.on_resize(size.width(), size.height())
        self.render()

    def update_view_matrix(self):
        self.view = np.eye(4, dtype=np.float32)
        translate(self.view, self.translation[0], self.translation[1], self.translation[2] )
        scale(self.view, self.scale, self.scale, self.scale)
        self.view = self.view.dot( np.array(q.matrix(self.rotation), dtype=np.float32) )
        # translate(self.view, -self.translation[0], -self.translation[1], -self.translation[2] )
        translate(self.view, 0,0, self.camera_position)
        for program in self.layer_manager.programs():
            program.setUniform('u_view', self.view)
            if program.draw_type == 'points':
                program.setUniform('u_model_scale', self.scale)

    def update_projection_matrix(self):
        view_width, view_height = [x/self.radius for x in self.size]

        if self.projection_mode == 'orthographic':
            self.projection = ortho(-view_width, view_width, -view_height, view_height, self.near_clip, self.far_clip)
        elif self.projection_mode == 'perspective':
            self.projection = perspective(self.fov, view_width / float(view_height), self.near_clip, self.far_clip)

        for program in self.layer_manager.programs():
            program.setUniform('u_projection', self.projection)

    def screen2view(self, x,y):
        width, height = self.size
        # print (x-width/2.)/self.radius, ((height-y)-height/2.)/self.radius
        return (x-width/2.)/self.radius, ((height-y)-height/2.)/self.radius

    def set_bg(self):
        gl.glEnable(gl.GL_BLEND)
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        if self.bg_white:
            gl.glClearColor(0.95,0.95,0.95,1)
            # gloo.set_state('translucent', clear_color=np.array([1,1,1,1]) )
        else:
            gl.glClearColor(0.05,0.05,0.05,1)
            # gloo.set_state('translucent', clear_color=np.array([0.15,0.15,0.15,1]) )

    def keyPressEvent(self, event):
        key = event.key()
        repeat = event.isAutoRepeat()
        if key == Qt.Key_R:
            self.view = np.eye(4, dtype=np.float32)
            self.rotation = q.quaternion()
            self.scale = self.modelscale
            self.camera_position = -12.
            self.near_clip = 2.
            self.far_clip = 100.
            self.center_view()
            self.update_projection_matrix()
        elif key == Qt.Key_Minus:
            for layer in self.layer_manager:
                if layer.is_visible:
                    for program in layer:
                        if program.draw_type == 'points':
                            if (program.is_visible and self.multiview) or not self.multiview:
                                program.setUniform('u_point_size', program.uniforms['u_point_size']/1.2)
        elif key == Qt.Key_Equal:
            for layer in self.layer_manager:
                if layer.is_visible:
                    for program in layer:
                        if program.draw_type == 'points':
                            if (program.is_visible and self.multiview) or not self.multiview:
                                program.setUniform('u_point_size', program.uniforms['u_point_size']*1.2)
        elif key == Qt.Key_B:
            for program in self.layer_manager.programs():
                if program.is_visible and program.draw_type == 'points':
                    program.do_blending = not program.do_blending
        elif key == Qt.Key_C:
            self.near_clip = -self.camera_position - 0.1
            self.far_clip = -self.camera_position + 0.1
            self.update_projection_matrix()
        elif key == Qt.Key_T:
            self.rotation = q.quaternion()
            self.update_view_matrix()
        elif key == Qt.Key_P:
            if self.projection_mode == 'perspective':
                self.projection_mode = 'orthographic'
            else:
                self.projection_mode = 'perspective'
            self.update_projection_matrix()
        elif key == Qt.Key_L:
            self.bg_white = not self.bg_white
            self.set_bg()
        elif Qt.Key_0 <= key <= Qt.Key_9:
            i = int(chr(key))-1
            layers = [item for item in self.layer_manager.layers.items()]
            if i < len(layers):
                name, layer = layers[i]
                self.set_layer_visibility(name,layer.toggle()) 

        #         if self.multiview:
        #             self.set_layer_visibility(list(self.data_programs.keys())[i], not list(self.data_programs.values())[i].is_visible)
        #         else:
        #             for pi, prog in enumerate(self.data_programs.values()):
        #                 prog.is_visible = False
        #                 if pi == i:
        #                     prog.is_visible = True

        self.update()

    def on_resize(self, size_x, size_y):
        gl.glViewport(int(0), int(0), int(size_x), int(size_y))

        self.radius = 0.5 * min(size_x, size_y)
        self.size = size_x, size_y

        self.update_projection_matrix()

    def wheelEvent(self, event):
    # def on_mouse_wheel(self, window, offset_x, offset_y):
        ticks = float(event.angleDelta().y()+event.angleDelta().x())/50
        modifiers = event.modifiers()

        if modifiers == Qt.ControlModifier | Qt.AltModifier:
            # if modifiers == Qt.ShiftModifier:
            ticks /= 30
            self.near_clip -= ticks
            self.far_clip -= ticks
            self.update_projection_matrix()
        elif modifiers == Qt.AltModifier:
            # if modifiers == Qt.ShiftModifier:
            ticks /= 30
            new = max(0.1,self.near_clip - ticks)
            if new <= self.far_clip:
                self.near_clip = new
                self.update_projection_matrix()
        elif modifiers == Qt.ControlModifier:
            # if modifiers == Qt.ShiftModifier:
            ticks /= 30
            new = min(1000,self.far_clip - ticks)
            if new >= self.near_clip:
                self.far_clip = new
                self.update_projection_matrix()
        elif modifiers == Qt.ShiftModifier:
            if self.projection_mode == 'perspective':
                old_fov = self.fov
                # do `dolly zooming` so that world appears at same size after canging fov
                self.fov = max(5.,self.fov + ticks)
                self.fov = min(120.,self.fov)
                self.camera_position = self.camera_position * (math.tan(math.radians(old_fov)/2.)) / (math.tan(math.radians(self.fov)/2.))
                self.update_projection_matrix()
                self.update_view_matrix()
        elif modifiers == Qt.MetaModifier:
            self.camera_position += ticks/10
            self.update_view_matrix()
        else:
            self.scale *= ticks/10 + 1.
            # self.camera_position += ticks/10
            self.update_view_matrix()
        self.update()

    def mouseMoveEvent(self, event):
        modifiers = event.modifiers()
        buttons = event.buttons()
        pos_x, pos_y = event.x(), event.y()

        if Qt.ShiftModifier == modifiers:
            x0,y0 = self.last_mouse_pos
            x1,y1 = pos_x, pos_y
            dx, dy = (x1-x0), (y1-y0)
            #scale to zero plane in projection frustrum
            if self.projection_mode == 'perspective':
                scale = -self.camera_position * math.tan(math.radians(self.fov/2.))
                dx, dy = scale*dx, scale*dy
                #multiply with inverse view matrix and apply translation in world coordinates
                self.translation += np.array([dx/self.radius, -dy/self.radius, 0., 0.]).dot( np.linalg.inv(self.view)) [:3]
            elif self.projection_mode == 'orthographic':
                # this is not fully correct
                self.translation += self.modelscale * np.array([dx, -dy, 0., 0.]).dot( np.linalg.inv(self.view)) [:3]
            
            self.hud_program.is_visible = True
        elif Qt.LeftButton == buttons:
            x0,y0 = self.screen2view(*self.last_mouse_pos)
            x1,y1 = self.screen2view(pos_x, pos_y)

            v0 = q.arcball(x0, y0)
            v1 = q.arcball(x1, y1)

            self.rotation = q.product(v1, v0, self.rotation)

            self.hud_program.is_visible = True
        else:
            self.hud_program.is_visible = False
        self.update_view_matrix()
        self.update()

        self.last_mouse_pos = pos_x, pos_y

    def update(self):
        self.render()
Beispiel #7
0
class ViewerWindow(QWindow):

    instructions = """
--Key controls
0-9     - toggle data layers
r       - reset view parameters
c       - clip view
t       - view from top
l       - light/dark background
=       - increase point size
-       - decrease point size

--Mouse controls
drag               
shift + move         - translate dataset
scroll               - scale dataset
shift + scroll       - change field of view
ctrl + scroll        - move far clipping plane
alt + scroll         - move near clipping plane
ctrl + alt + scroll  - move far and near clipping plane simultaniously
"""

    def __init__(self, parent=None, **kwargs):
        super(ViewerWindow, self).__init__(parent)
        self.setSurfaceType(QWindow.OpenGLSurface)

        format = QSurfaceFormat()
        format.setVersion(3, 3)
        format.setProfile(QSurfaceFormat.CoreProfile)
        format.setStereo(False)
        format.setSwapBehavior(QSurfaceFormat.DoubleBuffer)
        format.setDepthBufferSize(24)
        format.setSamples(16)

        self.context = QOpenGLContext(self)
        self.context.setFormat(format)
        if not self.context.create():
            raise Exception('self.context.create() failed')
        self.create()

        size = 720, 720
        self.resize(*size)

        self.context.makeCurrent(self)
        self.hud_program = CrossHairProgram()

        self.default_view = np.eye(4, dtype=np.float32)
        self.view = self.default_view
        self.model = np.eye(4, dtype=np.float32)
        self.projection = np.eye(4, dtype=np.float32)

        self.layer_manager = LayerManager()

        self.visibility_toggle_listeners = []
        self.multiview = True

        self.rotation = q.quaternion()
        self.scale = 0.6
        self.translation = np.zeros(3)

        self.radius = 0.5 * min(*size)
        self.fov = 5.
        self.camera_position = -12.
        self.near_clip = .1
        if 'near_clip' in kwargs:
            self.near_clip = kwargs['near_clip']
        self.far_clip = 100.
        if 'far_clip' in kwargs:
            self.far_clip = kwargs['far_clip']

        self.projection_mode = 'perspective'  # 'orthographic'

        self.size = size
        self.bg_white = False
        self.viewpoint_dict = {}

        print((self.instructions))

    def add_layer(self, layer):
        self.layer_manager.add_layer(layer)
        return layer

    # keeping this compatibility with older scripts
    def add_data_source(self,
                        name,
                        opts,
                        points,
                        normals=None,
                        radii=None,
                        intensity=None,
                        category=None,
                        zrange=None,
                        **kwargs):
        if len(self.layer_manager.layers) == 0:
            self.layer_manager.add_layer(Layer(name='Default'))
        self.layer_manager.layers['Default'].add_data_source(
            name,
            opts,
            points,
            normals=normals,
            radii=radii,
            intensity=intensity,
            category=category,
            zrange=zrange,
            **kwargs)

    def add_data_source_line(self, name, coords_start, coords_end, **args):
        if len(self.layer_manager.layers) == 0:
            self.layer_manager.add_layer(Layer(name='Default'))
        self.layer_manager.layers['Default'].add_data_source_line(
            name, coords_start, coords_end, **args)

    def add_data_source_triangle(self, name, coords, normals, **args):
        if len(self.layer_manager.layers) == 0:
            self.layer_manager.add_layer(Layer(name='Default'))
        self.layer_manager.layers['Default'].add_data_source_triangle(
            name, coords, normals, **args)

    def run(self):
        self.initialize()
        self.show()

    def set_layer_visibility(self, name, is_visible):
        for listener in self.visibility_toggle_listeners:
            listener(name, is_visible)

    def center_view(self, center=None):
        if center is None:
            center = self.layer_manager.first.get_center()
            data_range = self.layer_manager.first.data_range
            self.data_width = data_range[0]
            self.data_height = data_range[2]
        self.translation = np.zeros(3)
        self.model = np.eye(4, dtype=np.float32)
        translate(self.model, -center[0], -center[1], -center[2])
        for program in self.layer_manager.programs():
            program.setUniform('u_model', self.model)
        self.update_view_matrix()

    def initialize(self):
        self.context.makeCurrent(self)

        gl.glDepthMask(gl.GL_TRUE)
        gl.glEnable(gl.GL_DEPTH_TEST)
        gl.glDepthFunc(gl.GL_LESS)

        self.set_bg()

        view_width, view_height = [x / self.radius for x in self.size]

        self.center_view()
        for program in self.layer_manager.programs():
            program.setUniform('u_model', self.model)
            program.setUniform('u_view', self.view)
            program.setUniform('u_projection', self.projection)

        self.modelscale = .6 * 2 * min(2. * view_width / self.data_width,
                                       2. * view_height / self.data_height)
        self.scale = self.modelscale
        self.update_view_matrix()

        self.last_mouse_pos = 0, 0

        self.on_resize(*self.size)

    def render(self):
        if not self.isExposed():
            return
        self.context.makeCurrent(self)

        bits = 0
        bits |= gl.GL_COLOR_BUFFER_BIT
        bits |= gl.GL_DEPTH_BUFFER_BIT
        bits |= gl.GL_STENCIL_BUFFER_BIT
        gl.glClear(bits)
        gl.glEnable(gl.GL_PROGRAM_POINT_SIZE)

        for program in self.layer_manager.programs():
            if program.do_blending:
                if self.bg_white:
                    gl.glEnable(gl.GL_BLEND)
                    gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_SRC_ALPHA)
                else:
                    gl.glEnable(gl.GL_BLEND)
                    gl.glBlendFunc(gl.GL_ONE, gl.GL_SRC_ALPHA)
            else:
                gl.glDisable(gl.GL_BLEND)
        self.layer_manager.draw()

        if self.hud_program.is_visible:
            self.hud_program.draw()

        self.context.swapBuffers(self)

    def event(self, event):
        # print event.type()
        if event.type() == QEvent.UpdateRequest:
            self.render()
            return True

        return super(ViewerWindow, self).event(event)

    def exposeEvent(self, event):
        self.render()

    def resizeEvent(self, event):
        size = event.size()
        self.on_resize(size.width(), size.height())
        self.render()

    def update_view_matrix(self):
        self.view = np.eye(4, dtype=np.float32)
        translate(self.view, self.translation[0], self.translation[1],
                  self.translation[2])
        scale(self.view, self.scale, self.scale, self.scale)
        self.view = self.view.dot(
            np.array(q.matrix(self.rotation), dtype=np.float32))
        # translate(self.view, -self.translation[0], -self.translation[1], -self.translation[2] )
        translate(self.view, 0, 0, self.camera_position)
        for program in self.layer_manager.programs():
            program.setUniform('u_view', self.view)
            if program.draw_type == 'points':
                program.setUniform('u_model_scale', self.scale)

    def update_projection_matrix(self):
        view_width, view_height = [x / self.radius for x in self.size]

        if self.projection_mode == 'orthographic':
            self.projection = ortho(-view_width, view_width, -view_height,
                                    view_height, self.near_clip, self.far_clip)
        elif self.projection_mode == 'perspective':
            self.projection = perspective(self.fov,
                                          view_width / float(view_height),
                                          self.near_clip, self.far_clip)

        for program in self.layer_manager.programs():
            program.setUniform('u_projection', self.projection)

    def screen2view(self, x, y):
        width, height = self.size
        # print (x-width/2.)/self.radius, ((height-y)-height/2.)/self.radius
        return (x - width / 2.) / self.radius, (
            (height - y) - height / 2.) / self.radius

    def set_bg(self):
        gl.glEnable(gl.GL_BLEND)
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        if self.bg_white:
            gl.glClearColor(0.95, 0.95, 0.95, 1)
            # gloo.set_state('translucent', clear_color=np.array([1,1,1,1]) )
        else:
            gl.glClearColor(0.05, 0.05, 0.05, 1)
            # gloo.set_state('translucent', clear_color=np.array([0.15,0.15,0.15,1]) )

    def keyPressEvent(self, event):
        key = event.key()
        repeat = event.isAutoRepeat()
        if key == Qt.Key_R:
            self.view = np.eye(4, dtype=np.float32)
            self.rotation = q.quaternion()
            self.scale = self.modelscale
            self.camera_position = -12.
            self.near_clip = 2.
            self.far_clip = 100.
            self.center_view()
            self.update_projection_matrix()
        elif key == Qt.Key_Minus:
            for layer in self.layer_manager:
                if layer.is_visible:
                    for program in layer:
                        if program.draw_type == 'points':
                            if (program.is_visible
                                    and self.multiview) or not self.multiview:
                                program.setUniform(
                                    'u_point_size',
                                    program.uniforms['u_point_size'] / 1.2)
        elif key == Qt.Key_Equal:
            for layer in self.layer_manager:
                if layer.is_visible:
                    for program in layer:
                        if program.draw_type == 'points':
                            if (program.is_visible
                                    and self.multiview) or not self.multiview:
                                program.setUniform(
                                    'u_point_size',
                                    program.uniforms['u_point_size'] * 1.2)
        elif key == Qt.Key_B:
            for program in self.layer_manager.programs():
                if program.is_visible and program.draw_type == 'points':
                    program.do_blending = not program.do_blending
        elif key == Qt.Key_C:
            self.near_clip = -self.camera_position - 0.1
            self.far_clip = -self.camera_position + 0.1
            self.update_projection_matrix()
        elif key == Qt.Key_T:
            self.rotation = q.quaternion()
            self.update_view_matrix()
        elif key == Qt.Key_P:
            if self.projection_mode == 'perspective':
                self.projection_mode = 'orthographic'
            else:
                self.projection_mode = 'perspective'
            self.update_projection_matrix()
        elif key == Qt.Key_L:
            self.bg_white = not self.bg_white
            self.set_bg()
        elif Qt.Key_0 <= key <= Qt.Key_9:
            i = int(chr(key)) - 1
            layers = [item for item in self.layer_manager.layers.items()]
            if i < len(layers):
                name, layer = layers[i]
                self.set_layer_visibility(name, layer.toggle())

        #         if self.multiview:
        #             self.set_layer_visibility(list(self.data_programs.keys())[i], not list(self.data_programs.values())[i].is_visible)
        #         else:
        #             for pi, prog in enumerate(self.data_programs.values()):
        #                 prog.is_visible = False
        #                 if pi == i:
        #                     prog.is_visible = True

        self.update()

    def on_resize(self, size_x, size_y):
        gl.glViewport(int(0), int(0), int(size_x), int(size_y))

        self.radius = 0.5 * min(size_x, size_y)
        self.size = size_x, size_y

        self.update_projection_matrix()

    def wheelEvent(self, event):
        # def on_mouse_wheel(self, window, offset_x, offset_y):
        ticks = float(event.angleDelta().y() + event.angleDelta().x()) / 50
        modifiers = event.modifiers()

        if modifiers == Qt.ControlModifier | Qt.AltModifier:
            # if modifiers == Qt.ShiftModifier:
            ticks /= 30
            self.near_clip -= ticks
            self.far_clip -= ticks
            self.update_projection_matrix()
        elif modifiers == Qt.AltModifier:
            # if modifiers == Qt.ShiftModifier:
            ticks /= 30
            new = max(0.1, self.near_clip - ticks)
            if new <= self.far_clip:
                self.near_clip = new
                self.update_projection_matrix()
        elif modifiers == Qt.ControlModifier:
            # if modifiers == Qt.ShiftModifier:
            ticks /= 30
            new = min(1000, self.far_clip - ticks)
            if new >= self.near_clip:
                self.far_clip = new
                self.update_projection_matrix()
        elif modifiers == Qt.ShiftModifier:
            if self.projection_mode == 'perspective':
                old_fov = self.fov
                # do `dolly zooming` so that world appears at same size after canging fov
                self.fov = max(5., self.fov + ticks)
                self.fov = min(120., self.fov)
                self.camera_position = self.camera_position * (math.tan(
                    math.radians(old_fov) / 2.)) / (math.tan(
                        math.radians(self.fov) / 2.))
                self.update_projection_matrix()
                self.update_view_matrix()
        elif modifiers == Qt.MetaModifier:
            self.camera_position += ticks / 10
            self.update_view_matrix()
        else:
            self.scale *= ticks / 10 + 1.
            # self.camera_position += ticks/10
            self.update_view_matrix()
        self.update()

    def mouseMoveEvent(self, event):
        modifiers = event.modifiers()
        buttons = event.buttons()
        pos_x, pos_y = event.x(), event.y()

        if Qt.ShiftModifier == modifiers:
            x0, y0 = self.last_mouse_pos
            x1, y1 = pos_x, pos_y
            dx, dy = (x1 - x0), (y1 - y0)
            #scale to zero plane in projection frustrum
            if self.projection_mode == 'perspective':
                scale = -self.camera_position * math.tan(
                    math.radians(self.fov / 2.))
                dx, dy = scale * dx, scale * dy
                #multiply with inverse view matrix and apply translation in world coordinates
                self.translation += np.array(
                    [dx / self.radius, -dy / self.radius, 0.,
                     0.]).dot(np.linalg.inv(self.view))[:3]
            elif self.projection_mode == 'orthographic':
                # this is not fully correct
                self.translation += self.modelscale * np.array(
                    [dx, -dy, 0., 0.]).dot(np.linalg.inv(self.view))[:3]

            self.hud_program.is_visible = True
        elif Qt.LeftButton == buttons:
            x0, y0 = self.screen2view(*self.last_mouse_pos)
            x1, y1 = self.screen2view(pos_x, pos_y)

            v0 = q.arcball(x0, y0)
            v1 = q.arcball(x1, y1)

            self.rotation = q.product(v1, v0, self.rotation)

            self.hud_program.is_visible = True
        else:
            self.hud_program.is_visible = False
        self.update_view_matrix()
        self.update()

        self.last_mouse_pos = pos_x, pos_y

    def update(self):
        self.render()