def mix_colors(c1: QColor, c2: QColor, k: float):
     f = easing.sine
     red = f(c1.redF(), c2.redF(), k)
     green = f(c1.greenF(), c2.greenF(), k)
     blue = f(c1.blueF(), c2.blueF(), k)
     mixed = QColor(int(red * 255), int(green * 255), int(blue * 255))
     return mixed
 def resetBackgroundColor(self):
     self._backgroundColor = QColor(0, 0, 0, 0)
     self.gradient_color1 = QColor(0, 0, 0, 0)
     value = QColor(0, 0, 0, 0)
     self.gradient_color1 = (value.redF(), value.greenF(), value.blueF())
     self.colors['back'] = (value.redF(), value.greenF(), value.blueF())
     self.updateGL()
Exemple #3
0
    def mix(c1: QtGui.QColor, c2: QtGui.QColor, bias: float=0.5):
        def mixQreal(a, b, bias):
            return a + (b-a) * bias

        if bias <= 0:
            return c1
        elif bias >= 1:
            return c2
        
        r = mixQreal(c1.redF(), c2.redF(), bias)
        g = mixQreal(c1.greenF(), c2.greenF(), bias)
        b = mixQreal(c1.blueF(), c2.blueF(), bias)
        a = mixQreal(c1.alphaF(), c2.alphaF(), bias)
        
        return QtGui.QColor.fromRgbF(r, g, b, a)
def findDominantColor(listOfColors=[QColor()]):
    dominantColor = QColor()
    listOfColorNames = {}

    for color in listOfColors:
        count = listOfColorNames.get(color.name(), 0)
        listOfColorNames[color.name()] = count + 1

    # Check if there's a sense of dominant color:
    clear_dominant = False

    if len(listOfColorNames) == 2 and len(listOfColors) == 1:
        clear_dominant = True
    elif len(listOfColorNames) == 3 and len(listOfColors) == 2:
        clear_dominant = True
    elif len(listOfColorNames.keys()) < (len(listOfColors) * 0.5):
        clear_dominant = True

    if clear_dominant:
        namesSorted = sorted(listOfColorNames,
                             key=listOfColorNames.get,
                             reverse=True)
        dominantColor = QColor(namesSorted[0])
    else:
        for color in listOfColors:
            dominantColor.setRedF(0.5 * (dominantColor.redF() + color.redF()))
            dominantColor.setGreenF(0.5 *
                                    (dominantColor.greenF() + color.greenF()))
            dominantColor.setBlueF(0.5 *
                                   (dominantColor.blueF() + color.blueF()))

    return dominantColor
 def figure_out_type(svg=QDomElement()):
     type = None
     skipList = ["speech", "emphasis", "strong", "inverted", "general"]
     if svg.attribute("text-anchor") == "middle" or svg.attribute(
             "text-align") == "center":
         if "acbfStyles" in configDictionary.keys():
             stylesDictionary = configDictionary.get("acbfStyles", {})
             for key in stylesDictionary.keys():
                 if key not in skipList:
                     style = stylesDictionary.get(key, {})
                     font = style.get("font", "")
                     if isinstance(fonts, list):
                         if svg.attribute("family") in font:
                             type = key
                     elif svg.attribute("family") == font:
                         type = key
         else:
             type = None
     elif svg.attribute("text-align") == "justified":
         type = "formal"
     else:
         type = "commentary"
     inverted = None
     #Figure out whether this is inverted colored text.
     if svg.hasAttribute("fill"):
         stylesDictionary = configDictionary.get("acbfStyles", {})
         key = stylesDictionary.get("general", {})
         regular = QColor(key.get("color", "#000000"))
         key = stylesDictionary.get("inverted", {})
         invertedColor = QColor(key.get("color", "#FFFFFF"))
         textColor = QColor(svg.attribute("fill"))
         # Proceed to get luma for the three colors.
         lightnessR = (0.21 * regular.redF()) + (
             0.72 * regular.greenF()) + (0.07 * regular.blueF())
         lightnessI = (0.21 * invertedColor.redF()) + (
             0.72 * invertedColor.greenF()) + (0.07 * invertedColor.blueF())
         lightnessT = (0.21 * textColor.redF()) + (
             0.72 * textColor.greenF()) + (0.07 * textColor.blueF())
         if lightnessI > lightnessR:
             if lightnessT > (lightnessI + lightnessR) * 0.5:
                 inverted = "true"
         else:
             if lightnessT < (lightnessI + lightnessR) * 0.5:
                 inverted = "true"
     return [type, inverted]
Exemple #6
0
def font_color(bg_color: QtG.QColor) -> QtG.QColor:
    """Computes the font color that will yeild the best contrast with the given background color.

    @see
    https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color

    :param bg_color: Background color.
    :return: Font color with highest contrast.
    """
    luminance = 0.2126 * bg_color.redF() + 0.7152 * bg_color.greenF() + 0.0722 * bg_color.blueF()
    return _BLACK if luminance > 0.179 else _WHITE
Exemple #7
0
def to_occ_color(color) -> Quantity_Color:

    if not isinstance(color, QColor):
        if isinstance(color, tuple):
            if isinstance(color[0], int):
                color = QColor(*color)
            elif isinstance(color[0], float):
                color = QColor.fromRgbF(*color)
            else:
                raise ValueError('Unknown color format')
        else:
            color = QColor(color)

    return Quantity_Color(color.redF(), color.greenF(), color.blueF(), TOC_RGB)
Exemple #8
0
    def set_viewer_color(self):
        if self.set_viewer_bgr:
            color = QColor(self.viewer_bgr_color)
            c = (color.redF(), color.greenF(), color.blueF(), color.alphaF())

            nc = Ncat(TCP_IP, TCP_PORT)
            nc.connect()
            try:
                # BACKGROUND VIEWER f f f f
                nc.send('BACKGROUND VIEWER {:.4f} {:.4f} {:.4f} {:.4f};'.format(*c))
            except Exception as e:
                LOGGER.error('Sending viewer size command failed.\n%s', e)

            nc.close()
 def __propertyBackgroundChanged(self, name: str, color: QtGui.QColor):
     pandaQueue.put(
         PandaCommands.SetBackgroundColor(color.redF(), color.greenF(),
                                          color.blueF()))
Exemple #10
0
class GLWidget(QOpenGLWidget):

    clicked = pyqtSignal()

    PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2)

    vsrc = """
attribute highp vec4 vertex;
attribute mediump vec4 texCoord;
varying mediump vec4 texc;
uniform mediump mat4 matrix;
void main(void)
{
    gl_Position = matrix * vertex;
    texc = texCoord;
}
"""

    fsrc = """
uniform sampler2D texture;
varying mediump vec4 texc;
void main(void)
{
    gl_FragColor = texture2D(texture, texc.st);
}
"""

    coords = (
        (( +1, -1, -1 ), ( -1, -1, -1 ), ( -1, +1, -1 ), ( +1, +1, -1 )),
        (( +1, +1, -1 ), ( -1, +1, -1 ), ( -1, +1, +1 ), ( +1, +1, +1 )),
        (( +1, -1, +1 ), ( +1, -1, -1 ), ( +1, +1, -1 ), ( +1, +1, +1 )),
        (( -1, -1, -1 ), ( -1, -1, +1 ), ( -1, +1, +1 ), ( -1, +1, -1 )),
        (( +1, -1, +1 ), ( -1, -1, +1 ), ( -1, -1, -1 ), ( +1, -1, -1 )),
        (( -1, -1, +1 ), ( +1, -1, +1 ), ( +1, +1, +1 ), ( -1, +1, +1 ))
    )

    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        self.clearColor = QColor(Qt.black)
        self.xRot = 0
        self.yRot = 0
        self.zRot = 0
        self.program = None

        self.lastPos = QPoint()

    def minimumSizeHint(self):
        return QSize(50, 50)

    def sizeHint(self):
        return QSize(200, 200)

    def rotateBy(self, xAngle, yAngle, zAngle):
        self.xRot += xAngle
        self.yRot += yAngle
        self.zRot += zAngle
        self.update()

    def setClearColor(self, color):
        self.clearColor = color
        self.update()

    def initializeGL(self):
        self.gl = self.context().versionFunctions()
        self.gl.initializeOpenGLFunctions()

        self.makeObject()

        self.gl.glEnable(self.gl.GL_DEPTH_TEST)
        self.gl.glEnable(self.gl.GL_CULL_FACE)

        vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
        vshader.compileSourceCode(self.vsrc)

        fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
        fshader.compileSourceCode(self.fsrc)

        self.program = QOpenGLShaderProgram()
        self.program.addShader(vshader)
        self.program.addShader(fshader)
        self.program.bindAttributeLocation('vertex',
                self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.bindAttributeLocation('texCoord',
                self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.link()

        self.program.bind()
        self.program.setUniformValue('texture', 0)

        self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE,
                self.vertices)
        self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE,
                self.texCoords)

    def paintGL(self):
        self.gl.glClearColor(self.clearColor.redF(), self.clearColor.greenF(),
                self.clearColor.blueF(), self.clearColor.alphaF())
        self.gl.glClear(
                self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT)

        m = QMatrix4x4()
        m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0)
        m.translate(0.0, 0.0, -10.0)
        m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0)
        m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0)
        m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0)

        self.program.setUniformValue('matrix', m)

        for i, texture in enumerate(self.textures):
            texture.bind()
            self.gl.glDrawArrays(self.gl.GL_TRIANGLE_FAN, i * 4, 4)

    def resizeGL(self, width, height):
        side = min(width, height)
        self.gl.glViewport((width - side) // 2, (height - side) // 2, side,
                side)

    def mousePressEvent(self, event):
        self.lastPos = event.pos()

    def mouseMoveEvent(self, event):
        dx = event.x() - self.lastPos.x()
        dy = event.y() - self.lastPos.y()

        if event.buttons() & Qt.LeftButton:
            self.rotateBy(8 * dy, 8 * dx, 0)
        elif event.buttons() & Qt.RightButton:
            self.rotateBy(8 * dy, 0, 8 * dx)

        self.lastPos = event.pos()

    def mouseReleaseEvent(self, event):
        self.clicked.emit()

    def makeObject(self):
        self.textures = []
        self.texCoords = []
        self.vertices = []

        root = QFileInfo(__file__).absolutePath()

        for i in range(6):
            self.textures.append(
                    QOpenGLTexture(
                            QImage(root + ('/images/side%d.png' % (i + 1))).mirrored()))

            for j in range(4):
                self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))

                x, y, z = self.coords[i][j]
                self.vertices.append((0.2 * x, 0.2 * y, 0.2 * z))
Exemple #11
0
class QtRenderer(Renderer):
    def __init__(self):
        super().__init__()

        self._controller = Application.getInstance().getController()
        self._scene = self._controller.getScene()

        self._vertex_buffer_cache = {}
        self._index_buffer_cache = {}

        self._initialized = False

        self._light_position = Vector(0, 0, 0)
        self._background_color = QColor(128, 128, 128)
        self._viewport_width = 0
        self._viewport_height = 0
        self._window_width = 0
        self._window_height = 0

        self._batches = []

        self._quad_buffer = None

        self._camera = None

    initialized = Signal()

    ##  Get an integer multiplier that can be used to correct for screen DPI.
    def getPixelMultiplier(self):
        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(
            Application.getInstance().primaryScreen().physicalDotsPerInch() /
            96.0)

    ##  Get the list of render batches.
    def getBatches(self):
        return self._batches

    ##  Overridden from Renderer.
    #
    #   This makes sure the added render pass has the right size.
    def addRenderPass(self, render_pass):
        super().addRenderPass(render_pass)
        render_pass.setSize(self._viewport_width, self._viewport_height)

    ##  Set background color used when rendering.
    def setBackgroundColor(self, color):
        self._background_color = color

    def getViewportWidth(self):
        return self._viewport_width

    def getViewportHeight(self):
        return self._viewport_height

    ##  Set the viewport size.
    #
    #   \param width The new width of the viewport.
    #   \param height The new height of the viewport.
    def setViewportSize(self, width, height):
        self._viewport_width = width
        self._viewport_height = height

        for render_pass in self._render_passes:
            render_pass.setSize(width, height)

    ##  Set the window size.
    def setWindowSize(self, width, height):
        self._window_width = width
        self._window_height = height

    ##  Get the window size.
    #
    #   \return A tuple of (window_width, window_height)
    def getWindowSize(self):
        return (self._window_width, self._window_height)

    ##  Overrides Renderer::beginRendering()
    def beginRendering(self):
        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(),
                              self._background_color.greenF(),
                              self._background_color.blueF(),
                              self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT
                         | self._gl.GL_DEPTH_BUFFER_BIT)
        self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)

    ##  Overrides Renderer::queueNode()
    def queueNode(self, node, **kwargs):
        type = kwargs.pop("type", RenderBatch.RenderType.Solid)
        if kwargs.pop("transparent", False):
            type = RenderBatch.RenderType.Transparent
        elif kwargs.pop("overlay", False):
            type = RenderBatch.RenderType.Overlay

        shader = kwargs.pop("shader", self._default_material)
        batch = RenderBatch(shader, type=type, **kwargs)

        batch.addItem(node.getWorldTransformation(),
                      kwargs.get("mesh", node.getMeshData()),
                      kwargs.pop("uniforms", None))

        self._batches.append(batch)

    ##  Overrides Renderer::render()
    def render(self):
        self._batches.sort()

        for render_pass in self.getRenderPasses():
            render_pass.render()

    ##  Overrides Renderer::endRendering()
    def endRendering(self):
        self._batches.clear()

    ##  Render a full screen quad.
    #
    #   \param shader The shader to use when rendering.
    def renderFullScreenQuad(self, shader):
        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_BLEND)

        shader.setUniformValue("u_modelViewProjectionMatrix", Matrix())

        self._quad_buffer.bind()

        shader.enableAttribute("a_vertex", "vector3f", 0)
        shader.enableAttribute("a_uvs", "vector2f", 72)

        self._gl.glDrawArrays(self._gl.GL_TRIANGLES, 0, 6)

        shader.disableAttribute("a_vertex")
        shader.disableAttribute("a_uvs")
        self._quad_buffer.release()

    def _initialize(self):
        OpenGL.setInstance(QtOpenGL())
        self._gl = OpenGL.getInstance().getBindingsObject()

        self._default_material = OpenGL.getInstance().createShaderProgram(
            Resources.getPath(Resources.Shaders, "default.shader"))

        self._render_passes.add(
            DefaultPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(
            SelectionPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(
            CompositePass(self._viewport_width, self._viewport_height))

        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()
        buffer.allocate(120)
        data = numpy.array([
            -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, -1.0, -1.0, 0.0,
            1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0,
            0.0, 1.0, 0.0, 1.0, 1.0
        ],
                           dtype=numpy.float32).tostring()
        buffer.write(0, data, len(data))
        buffer.release()
        self._quad_buffer = buffer

        self._initialized = True
        self.initialized.emit()
Exemple #12
0
class QtGL2Renderer(Renderer):
    def __init__(self):
        super().__init__()

        self._controller = Application.getInstance().getController()
        self._scene = self._controller.getScene()

        self._vertex_buffer_cache = {}
        self._index_buffer_cache = {}

        self._initialized = False

        self._light_position = Vector(0, 0, 0)
        self._background_color = QColor(128, 128, 128)
        self._viewport_width = 0
        self._viewport_height = 0
        self._window_width = 0
        self._window_height = 0

        self._solids_queue = []
        self._transparent_queue = []
        self._overlay_queue = []

        self._render_selection = True
        self._selection_buffer = None
        self._selection_map = {}
        self._selection_image = None

        self._camera = None

    def getPixelMultiplier(self):
        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(
            Application.getInstance().primaryScreen().physicalDotsPerInch() /
            96.0)

    ##  Create a new material
    #   \param vert
    #   \param frag
    #   \return material
    def createMaterial(self, vert, frag):
        mat = QtGL2Material.QtGL2Material(self)
        mat.loadVertexShader(vert)
        mat.loadFragmentShader(frag)
        mat.build()
        return mat

    ##  Create frame buffer with given width/height
    #   \param width Width of buffer
    #   \param height Height of buffer
    #   \return Created frame buffer
    def createFrameBuffer(self, width, height):
        buffer_format = QOpenGLFramebufferObjectFormat()
        buffer_format.setAttachment(QOpenGLFramebufferObject.Depth)
        return QOpenGLFramebufferObject(width, height, buffer_format)

    ##  Set light position of global light
    def setLightPosition(self, position):
        self._light_position = position

    ##  Set background color of the rendering.
    def setBackgroundColor(self, color):
        self._background_color = color

    def setViewportSize(self, width, height):
        self._viewport_width = width
        self._viewport_height = height

    def setWindowSize(self, width, height):
        self._window_width = width
        self._window_height = height

    ##  Reset the selection image, so a redraw is forced.
    #   This is used when the scene is changed by delete actions, so the image needs to be redrawn.
    #   It can happen that mouse events fire much faster than rendering, which can cause problems
    #   if the old selection image is still used.
    def resetSelectionImage(self):
        self._selection_image = None

    ##  Get the selection colors within a radius.
    #   All objects (either full objects or single points in a cloud are drawn with an unique color.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    #   \param radius Radius in pixels to select.
    #   \return list of colors ARGB (values from 0 to 1.)
    def getSelectionColorAtCoorindateRadius(self, x, y, radius):
        if not self._selection_image:
            return None
        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height
        squared_radius = radius * radius
        samples = []
        for sx in range(-radius, radius):
            squared_sx = sx * sx
            if px + sx < 0 or px + sx > (self._selection_image.width() - 1):
                continue
            for sy in range(-radius, radius):
                squared_sy = sy * sy
                if py + sy < 0 or py + sy > (self._selection_image.height() -
                                             1):
                    continue
                if squared_sx + squared_sy < squared_radius:
                    pixel = self._selection_image.pixel(px + sx, py + sy)
                    samples.append(Color.fromARGB(pixel))
        return samples

    ##  Get the selection colors on coordinate
    #   All objects (either full objects or single points in a cloud are drawn with an unique color.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    #   \return color ARGB (values from 0 to 1.)
    def getSelectionColorAtCoordinate(self, x, y):
        if not self._selection_image:
            return None
        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height
        return Color.fromARGB(self._selection_image.pixel(px, py))

    ##  Get object ID at coordinate.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    def getIdAtCoordinate(self, x, y):
        if not self._selection_image:
            return None

        px = (0.5 + x / 2.0) * self._window_width
        py = (0.5 + y / 2.0) * self._window_height

        if px < 0 or px > (self._selection_image.width() -
                           1) or py < 0 or py > (
                               self._selection_image.height() - 1):
            return None

        pixel = self._selection_image.pixel(px, py)
        return self._selection_map.get(Color.fromARGB(pixel), None)

    ##  Render selection is used to 'highlight' the selected objects
    def setRenderSelection(self, render):
        self._render_selection = render

    def beginRendering(self):
        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(),
                              self._background_color.greenF(),
                              self._background_color.blueF(),
                              self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT
                         | self._gl.GL_DEPTH_BUFFER_BIT)

        if not QOpenGLContext.currentContext().format().renderableType(
        ) == QSurfaceFormat.OpenGLES:
            self._gl.glPointSize(2)

        self._solids_queue.clear()
        self._transparent_queue.clear()
        self._overlay_queue.clear()

        self._render_selection = True

    ##  Put a node in the render queue
    def queueNode(self, node, **kwargs):
        queue_item = {"node": node}

        if "mesh" in kwargs:
            queue_item["mesh"] = kwargs["mesh"]

        queue_item["material"] = kwargs.get("material", self._default_material)

        mode = kwargs.get("mode", Renderer.RenderTriangles)
        if mode is Renderer.RenderLines:
            queue_item["mode"] = self._gl.GL_LINES
        elif mode is Renderer.RenderLineLoop:
            queue_item["mode"] = self._gl.GL_LINE_LOOP
        elif mode is Renderer.RenderPoints:
            queue_item["mode"] = self._gl.GL_POINTS
        else:
            queue_item["mode"] = self._gl.GL_TRIANGLES

        queue_item["wireframe"] = (mode == Renderer.RenderWireframe)

        queue_item["force_single_sided"] = kwargs.get("force_single_sided",
                                                      False)

        if kwargs.get("end", None):
            queue_item["range"] = [kwargs.get("start", 0), kwargs.get("end")]

        if kwargs.get("transparent", False):
            self._transparent_queue.append(queue_item)
        elif kwargs.get("overlay", False):
            self._overlay_queue.append(queue_item)
        else:
            self._solids_queue.append(queue_item)

    ##  Render all nodes in the queue
    def renderQueuedNodes(self):
        self._gl.glEnable(self._gl.GL_DEPTH_TEST)
        self._gl.glDepthFunc(self._gl.GL_LESS)
        self._gl.glDepthMask(self._gl.GL_TRUE)
        self._gl.glDisable(self._gl.GL_CULL_FACE)
        self._gl.glLineWidth(self.getPixelMultiplier())

        self._scene.acquireLock()

        self._camera = self._scene.getActiveCamera()
        if not self._camera:
            Logger.log("e", "No active camera set, can not render")
            self._scene.releaseLock()
            return

        # Render the selection image
        selectable_nodes = []
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.isSelectable() and node.getMeshData():
                selectable_nodes.append(node)

        if not selectable_nodes:
            self._selection_image = None

        if selectable_nodes:
            #TODO: Use a limited area around the mouse rather than a full viewport for rendering
            if self._selection_buffer.width(
            ) != self._viewport_width or self._selection_buffer.height(
            ) != self._viewport_height:
                self._selection_buffer = self.createFrameBuffer(
                    self._viewport_width, self._viewport_height)

            self._selection_buffer.bind()
            self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)
            self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT
                             | self._gl.GL_DEPTH_BUFFER_BIT)
            self._gl.glDisable(self._gl.GL_BLEND)
            self._selection_map.clear()
            for node in selectable_nodes:
                if type(
                        node
                ) is PointCloudNode:  #Pointcloud node sets vertex color (to be used for point precise selection)
                    self._renderItem({
                        "node": node,
                        "material": self._handle_material,
                        "mode": self._gl.GL_POINTS
                    })
                else:
                    color = self._getObjectColor(node)
                    self._selection_map[color] = id(node)
                    self._selection_material.setUniformValue("u_color", color)
                    self._renderItem({
                        "node": node,
                        "material": self._selection_material,
                        "mode": self._gl.GL_TRIANGLES
                    })
            tool = self._controller.getActiveTool()
            if tool:
                tool_handle = tool.getHandle()
                if tool_handle and tool_handle.getSelectionMesh(
                ) and tool_handle.getParent():
                    self._selection_map.update(tool_handle.getSelectionMap())
                    self._gl.glDisable(self._gl.GL_DEPTH_TEST)
                    self._renderItem({
                        "node": tool_handle,
                        "mesh": tool_handle.getSelectionMesh(),
                        "material": self._handle_material,
                        "mode": self._gl.GL_TRIANGLES
                    })
                    self._gl.glEnable(self._gl.GL_DEPTH_TEST)

            self._selection_image = self._selection_buffer.toImage()
            self._selection_buffer.release()

        # Workaround for a MacOSX Intel HD Graphics 6000 Bug
        # Apparently, performing a glReadPixels call on a bound framebuffer breaks releasing the
        # depth buffer, which means the rest of the depth tested geometry never renders. To work-
        # around this we perform a clear here. Note that for some reason we also need to set
        # glClearColor since it is apparently not stored properly.
        self._gl.glClearColor(self._background_color.redF(),
                              self._background_color.greenF(),
                              self._background_color.blueF(),
                              self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT
                         | self._gl.GL_DEPTH_BUFFER_BIT)

        self._gl.glEnable(self._gl.GL_STENCIL_TEST)
        self._gl.glStencilMask(0xff)
        self._gl.glClearStencil(0)
        self._gl.glClear(self._gl.GL_STENCIL_BUFFER_BIT)
        self._gl.glStencilFunc(self._gl.GL_ALWAYS, 0xff, 0xff)
        self._gl.glStencilOp(self._gl.GL_REPLACE, self._gl.GL_REPLACE,
                             self._gl.GL_REPLACE)
        self._gl.glStencilMask(0)

        for item in self._solids_queue:
            if Selection.isSelected(item["node"]):
                self._gl.glStencilMask(0xff)
                self._renderItem(item)
                self._gl.glStencilMask(0)
            else:
                self._renderItem(item)

        if self._render_selection:
            self._gl.glStencilMask(0)
            self._gl.glStencilFunc(self._gl.GL_EQUAL, 0, 0xff)
            self._gl.glLineWidth(2 * self.getPixelMultiplier())
            for node in Selection.getAllSelectedObjects():
                if node.getMeshData() and type(node) is not PointCloudNode:
                    self._renderItem({
                        "node": node,
                        "material": self._outline_material,
                        "mode": self._gl.GL_TRIANGLES,
                        "wireframe": True
                    })

            self._gl.glLineWidth(self.getPixelMultiplier())

        self._gl.glDisable(self._gl.GL_STENCIL_TEST)
        self._gl.glDepthMask(self._gl.GL_FALSE)
        self._gl.glEnable(self._gl.GL_BLEND)
        self._gl.glEnable(self._gl.GL_CULL_FACE)
        self._gl.glBlendFunc(self._gl.GL_SRC_ALPHA,
                             self._gl.GL_ONE_MINUS_SRC_ALPHA)

        for item in self._transparent_queue:
            self._renderItem(item)

        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_CULL_FACE)

        for item in self._overlay_queue:
            self._renderItem(item)

        self._scene.releaseLock()

    def endRendering(self):
        pass

    def _initialize(self):
        profile = QOpenGLVersionProfile()
        profile.setVersion(2, 0)
        self._gl = QOpenGLContext.currentContext().versionFunctions(profile)
        self._gl.initializeOpenGLFunctions()

        self._default_material = self.createMaterial(
            Resources.getPath(Resources.Shaders, "default.vert"),
            Resources.getPath(Resources.Shaders, "default.frag"))

        self._default_material.setUniformValue("u_ambientColor",
                                               Color(0.3, 0.3, 0.3, 1.0))
        self._default_material.setUniformValue("u_diffuseColor",
                                               Color(0.5, 0.5, 0.5, 1.0))
        self._default_material.setUniformValue("u_specularColor",
                                               Color(0.7, 0.7, 0.7, 1.0))
        self._default_material.setUniformValue("u_shininess", 20.)

        self._selection_buffer = self.createFrameBuffer(128, 128)
        self._selection_material = self.createMaterial(
            Resources.getPath(Resources.Shaders, "basic.vert"),
            Resources.getPath(Resources.Shaders, "color.frag"))

        self._handle_material = self.createMaterial(
            Resources.getPath(Resources.Shaders, "basic.vert"),
            Resources.getPath(Resources.Shaders, "vertexcolor.frag"))

        self._outline_material = self.createMaterial(
            Resources.getPath(Resources.Shaders, "outline.vert"),
            Resources.getPath(Resources.Shaders, "outline.frag"))

        self._initialized = True

    def _renderItem(self, item):
        node = item["node"]
        mesh = item.get("mesh", node.getMeshData())
        if not mesh:
            return  #Something went wrong, node has no mesh.
        transform = node.getWorldTransformation()
        material = item["material"]
        mode = item["mode"]
        wireframe = item.get("wireframe", False)
        range = item.get("range", None)

        culling_enabled = self._gl.glIsEnabled(self._gl.GL_CULL_FACE)
        if item.get("force_single_sided") and not culling_enabled:
            self._gl.glEnable(self._gl.GL_CULL_FACE)

        material.bind()
        material.setUniformValue("u_projectionMatrix",
                                 self._camera.getProjectionMatrix(),
                                 cache=False)
        material.setUniformValue(
            "u_viewMatrix",
            self._camera.getWorldTransformation().getInverse(),
            cache=False)
        material.setUniformValue("u_viewPosition",
                                 self._camera.getWorldPosition(),
                                 cache=False)
        material.setUniformValue("u_modelMatrix", transform, cache=False)
        material.setUniformValue("u_lightPosition",
                                 self._camera.getWorldPosition() +
                                 Vector(0, 50, 0),
                                 cache=False)

        if mesh.hasNormals():
            normal_matrix = copy.deepcopy(transform)
            normal_matrix.setRow(3, [0, 0, 0, 1])
            normal_matrix.setColumn(3, [0, 0, 0, 1])
            normal_matrix = normal_matrix.getInverse().getTransposed()
            material.setUniformValue("u_normalMatrix",
                                     normal_matrix,
                                     cache=False)

        vertex_buffer = None
        try:
            vertex_buffer = getattr(mesh, vertexBufferProperty)
        except AttributeError:
            pass

        if vertex_buffer is None:
            vertex_buffer = self._createVertexBuffer(mesh)

        vertex_buffer.bind()

        if mesh.hasIndices():
            index_buffer = None
            try:
                index_buffer = getattr(mesh, indexBufferProperty)
            except AttributeError:
                pass

            if index_buffer is None:
                index_buffer = self._createIndexBuffer(mesh)

            index_buffer.bind()

        material.enableAttribute("a_vertex", "vector3f", 0)
        offset = mesh.getVertexCount() * 3 * 4

        if mesh.hasNormals():
            material.enableAttribute("a_normal", "vector3f", offset)
            offset += mesh.getVertexCount() * 3 * 4

        if mesh.hasColors():
            material.enableAttribute("a_color", "vector4f", offset)
            offset += mesh.getVertexCount() * 4 * 4

        if mesh.hasUVCoordinates():
            material.enableAttribute("a_uvs", "vector2f", offset)
            offset += mesh.getVertexCount() * 2 * 4

        if wireframe and hasattr(self._gl, "glPolygonMode"):
            self._gl.glPolygonMode(self._gl.GL_FRONT_AND_BACK,
                                   self._gl.GL_LINE)

        if mesh.hasIndices():
            if range is None:
                if mode == self._gl.GL_TRIANGLES:
                    self._gl.glDrawElements(mode,
                                            mesh.getFaceCount() * 3,
                                            self._gl.GL_UNSIGNED_INT, None)
                else:
                    self._gl.glDrawElements(mode, mesh.getFaceCount(),
                                            self._gl.GL_UNSIGNED_INT, None)
            else:
                if mode == self._gl.GL_TRIANGLES:
                    self._gl.glDrawRangeElements(mode, range[0], range[1],
                                                 range[1] - range[0],
                                                 self._gl.GL_UNSIGNED_INT,
                                                 None)
                else:
                    self._gl.glDrawRangeElements(mode, range[0], range[1],
                                                 range[1] - range[0],
                                                 self._gl.GL_UNSIGNED_INT,
                                                 None)
        else:
            self._gl.glDrawArrays(mode, 0, mesh.getVertexCount())

        if wireframe and hasattr(self._gl, "glPolygonMode"):
            self._gl.glPolygonMode(self._gl.GL_FRONT_AND_BACK,
                                   self._gl.GL_FILL)

        material.disableAttribute("a_vertex")
        material.disableAttribute("a_normal")
        material.disableAttribute("a_color")
        material.disableAttribute("a_uvs")
        vertex_buffer.release()

        if mesh.hasIndices():
            index_buffer.release()

        material.release()

        if item.get("force_single_sided") and not culling_enabled:
            self._gl.glDisable(self._gl.GL_CULL_FACE)

    def _createVertexBuffer(self, mesh):
        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()

        buffer_size = mesh.getVertexCount(
        ) * 3 * 4  # Vertex count * number of components * sizeof(float32)
        if mesh.hasNormals():
            buffer_size += mesh.getVertexCount(
            ) * 3 * 4  # Vertex count * number of components * sizeof(float32)
        if mesh.hasColors():
            buffer_size += mesh.getVertexCount(
            ) * 4 * 4  # Vertex count * number of components * sizeof(float32)
        if mesh.hasUVCoordinates():
            buffer_size += mesh.getVertexCount(
            ) * 2 * 4  # Vertex count * number of components * sizeof(float32)

        buffer.allocate(buffer_size)

        offset = 0
        vertices = mesh.getVerticesAsByteArray()
        if vertices is not None:
            buffer.write(0, vertices, len(vertices))
            offset += len(vertices)

        if mesh.hasNormals():
            normals = mesh.getNormalsAsByteArray()
            buffer.write(offset, normals, len(normals))
            offset += len(normals)

        if mesh.hasColors():
            colors = mesh.getColorsAsByteArray()
            buffer.write(offset, colors, len(colors))
            offset += len(colors)

        if mesh.hasUVCoordinates():
            uvs = mesh.getUVCoordinatesAsByteArray()
            buffer.write(offset, uvs, len(uvs))
            offset += len(uvs)

        buffer.release()

        setattr(mesh, vertexBufferProperty, buffer)
        return buffer

    def _createIndexBuffer(self, mesh):
        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

        data = mesh.getIndicesAsByteArray()
        buffer.allocate(data, len(data))
        buffer.release()

        setattr(mesh, indexBufferProperty, buffer)
        return buffer

    ##  Create object color based on ID of node.
    #   \param node Node to get color for
    #   \return Color
    def _getObjectColor(self, node):
        obj_id = id(node)
        r = (obj_id & 0xff000000) >> 24
        g = (obj_id & 0x00ff0000) >> 16
        b = (obj_id & 0x0000ff00) >> 8
        a = (obj_id & 0x000000ff) >> 0
        return Color(r, g, b, a)
Exemple #13
0
class GLWidget(QOpenGLWidget):

    clicked = pyqtSignal()

    PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2)

    vsrc = """
attribute highp vec4 vertex;
attribute mediump vec4 texCoord;
varying mediump vec4 texc;
uniform mediump mat4 matrix;
void main(void)
{
    gl_Position = matrix * vertex;
    texc = texCoord;
}
"""

    fsrc = """
uniform sampler2D texture;
varying mediump vec4 texc;
void main(void)
{
    gl_FragColor = texture2D(texture, texc.st);
}
"""

    coords = (((+1, -1, -1), (-1, -1, -1), (-1, +1, -1), (+1, +1, -1)),
              ((+1, +1, -1), (-1, +1, -1), (-1, +1, +1), (+1, +1, +1)),
              ((+1, -1, +1), (+1, -1, -1), (+1, +1, -1), (+1, +1, +1)),
              ((-1, -1, -1), (-1, -1, +1), (-1, +1, +1),
               (-1, +1, -1)), ((+1, -1, +1), (-1, -1, +1), (-1, -1, -1),
                               (+1, -1, -1)), ((-1, -1, +1), (+1, -1, +1),
                                               (+1, +1, +1), (-1, +1, +1)))

    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        self.clearColor = QColor(Qt.black)
        self.xRot = 0
        self.yRot = 0
        self.zRot = 0
        self.program = None

        self.lastPos = QPoint()

    def minimumSizeHint(self):
        """
        Define the minimum size of the widget
        """
        return QSize(50, 50)

    def sizeHint(self):
        """
        Define a default size for the widget
        """
        return QSize(200, 200)

    def rotateBy(self, xAngle, yAngle, zAngle):
        self.xRot += xAngle
        self.yRot += yAngle
        self.zRot += zAngle
        self.update()

    def setClearColor(self, color):
        self.clearColor = color
        self.update()

    def initializeGL(self):
        self.gl = self.context().versionFunctions()
        self.gl.initializeOpenGLFunctions()

        self.makeObject()

        self.gl.glEnable(self.gl.GL_DEPTH_TEST)
        self.gl.glEnable(self.gl.GL_CULL_FACE)

        vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
        vshader.compileSourceCode(self.vsrc)

        fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
        fshader.compileSourceCode(self.fsrc)

        self.program = QOpenGLShaderProgram()
        self.program.addShader(vshader)
        self.program.addShader(fshader)
        self.program.bindAttributeLocation('vertex',
                                           self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.bindAttributeLocation('texCoord',
                                           self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.link()

        self.program.bind()
        self.program.setUniformValue('texture', 0)

        self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE,
                                       self.vertices)
        self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE,
                                       self.texCoords)

    def paintGL(self):
        self.gl.glClearColor(self.clearColor.redF(), self.clearColor.greenF(),
                             self.clearColor.blueF(), self.clearColor.alphaF())
        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT
                        | self.gl.GL_DEPTH_BUFFER_BIT)

        m = QMatrix4x4()
        m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0)
        m.translate(0.0, 0.0, -10.0)

        self.program.setUniformValue('matrix', m)

        self.texture.bind()
        self.gl.glDrawArrays(self.gl.GL_TRIANGLE_FAN, 0, 4)

    def resizeGL(self, width, height):
        side = min(width, height)
        self.gl.glViewport((width - side) // 2, (height - side) // 2, side,
                           side)

    def makeObject(self):
        self.texCoords = [(True, True), (False, True), (False, False),
                          (True, False)]
        self.vertices = [(0.5, -0.5, -0.5), (-0.5, -0.5, -0.5),
                         (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5)]

        my_movie = QImage('/Users/reno/Dropbox/media/cloudy.png')
        self.texture = QOpenGLTexture(my_movie.mirrored())
Exemple #14
0
class QtRenderer(Renderer):
    """A Renderer implementation using PyQt's OpenGL implementation to render."""

    def __init__(self) -> None:
        super().__init__()

        self._controller = Application.getInstance().getController()  # type: Controller
        self._scene = self._controller.getScene()  # type: Scene

        self._initialized = False  # type: bool

        self._light_position = Vector(0, 0, 0)  # type: Vector
        self._background_color = QColor(128, 128, 128)  # type: QColor
        self._viewport_width = 0  # type: int
        self._viewport_height = 0  # type: int
        self._window_width = 0  # type: int
        self._window_height = 0  # type: int

        self._batches = []  # type: List[RenderBatch]

        self._quad_buffer = None  # type: QOpenGLBuffer

        self._camera = None  # type: Optional[Camera]

    initialized = Signal()

    def getPixelMultiplier(self) -> int:
        """Get an integer multiplier that can be used to correct for screen DPI."""

        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(UM.Qt.QtApplication.QtApplication.getInstance().primaryScreen().physicalDotsPerInch() / 96.0)

    def getBatches(self) -> List[RenderBatch]:
        """Get the list of render batches."""

        return self._batches

    def addRenderPass(self, render_pass: "******") -> None:
        """Overridden from Renderer.

        This makes sure the added render pass has the right size.
        """

        super().addRenderPass(render_pass)
        render_pass.setSize(self._viewport_width, self._viewport_height)

    def setBackgroundColor(self, color: QColor) -> None:
        """Set background color used when rendering."""

        self._background_color = color

    def getViewportWidth(self) -> int:
        return self._viewport_width

    def getViewportHeight(self) -> int:
        return self._viewport_height

    def setViewportSize(self, width: int, height: int) -> None:
        """Set the viewport size.

        :param width: The new width of the viewport.
        :param height: The new height of the viewport.
        """

        self._viewport_width = width
        self._viewport_height = height

        for render_pass in self._render_passes:
            render_pass.setSize(width, height)

    def setWindowSize(self, width: int, height: int) -> None:
        """Set the window size."""

        self._window_width = width
        self._window_height = height

    def getWindowSize(self) -> Tuple[int, int]:
        """Get the window size.

        :return: A tuple of (window_width, window_height)
        """

        return self._window_width, self._window_height

    def beginRendering(self) -> None:
        """Overrides Renderer::beginRendering()"""

        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(), self._background_color.greenF(), self._background_color.blueF(), self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
        self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)

    def queueNode(self, node: "SceneNode", **kwargs) -> None:
        """Overrides Renderer::queueNode()"""

        type = kwargs.pop("type", RenderBatch.RenderType.Solid)
        if kwargs.pop("transparent", False):
            type = RenderBatch.RenderType.Transparent
        elif kwargs.pop("overlay", False):
            type = RenderBatch.RenderType.Overlay

        shader = kwargs.pop("shader", self._default_material)
        batch = RenderBatch(shader, type = type, **kwargs)

        batch.addItem(node.getWorldTransformation(), kwargs.get("mesh", node.getMeshData()), kwargs.pop("uniforms", None))

        self._batches.append(batch)

    def render(self) -> None:
        """Overrides Renderer::render()"""

        self._batches.sort()

        for render_pass in self.getRenderPasses():
            width, height = render_pass.getSize()
            self._gl.glViewport(0, 0, width, height)
            render_pass.render()

    def reRenderLast(self):
        """Sometimes not an *entire* new render is required (QML is updated, but the scene did not).
        In that case we ask the composite pass (which must be the last render pass) to render (instead of re-rendering
        all the passes & the views.
        """

        self.beginRendering() # First ensure that the viewport is set correctly.
        self.getRenderPasses()[-1].render()

    def endRendering(self) -> None:
        """Overrides Renderer::endRendering()"""

        self._batches.clear()

    def renderFullScreenQuad(self, shader: "ShaderProgram") -> None:
        """Render a full screen quad (rectangle).

        The function is used to draw render results on.
        :param shader: The shader to use when rendering.
        """

        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_BLEND)

        shader.setUniformValue("u_modelViewProjectionMatrix", Matrix())

        if OpenGLContext.properties["supportsVertexArrayObjects"]:
            vao = QOpenGLVertexArrayObject()
            vao.create()
            vao.bind()

        self._quad_buffer.bind()

        shader.enableAttribute("a_vertex", "vector3f", 0)
        shader.enableAttribute("a_uvs", "vector2f", 72)

        self._gl.glDrawArrays(self._gl.GL_TRIANGLES, 0, 6)

        shader.disableAttribute("a_vertex")
        shader.disableAttribute("a_uvs")
        self._quad_buffer.release()

    def _initialize(self) -> None:
        supports_vao = OpenGLContext.supportsVertexArrayObjects()  # fill the OpenGLContext.properties
        Logger.log("d", "Support for Vertex Array Objects: %s", supports_vao)

        OpenGL()
        self._gl = OpenGL.getInstance().getBindingsObject()

        self._default_material = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) #type: ShaderProgram

        self._render_passes.add(DefaultPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(SelectionPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(CompositePass(self._viewport_width, self._viewport_height))

        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()
        buffer.allocate(120)
        data = numpy.array([
            -1.0, -1.0, 0.0,
             1.0,  1.0, 0.0,
            -1.0,  1.0, 0.0,
            -1.0, -1.0, 0.0,
             1.0, -1.0, 0.0,
             1.0,  1.0, 0.0,
             0.0,  0.0,
             1.0,  1.0,
             0.0,  1.0,
             0.0,  0.0,
             1.0,  0.0,
             1.0,  1.0
        ], dtype = numpy.float32).tostring()
        buffer.write(0, data, len(data))
        buffer.release()
        self._quad_buffer = buffer

        self._initialized = True
        self.initialized.emit()
class QtGL2Renderer(Renderer):
    def __init__(self):
        super().__init__()

        self._controller = Application.getInstance().getController()
        self._scene = self._controller.getScene()

        self._vertex_buffer_cache = {}
        self._index_buffer_cache = {}

        self._initialized = False

        self._light_position = Vector(0, 0, 0)
        self._background_color = QColor(128, 128, 128)
        self._viewport_width = 0
        self._viewport_height = 0

        self._solids_queue = []
        self._transparent_queue = []
        self._overlay_queue = []

        self._render_selection = True
        self._selection_buffer = None
        self._selection_map = {}
        self._selection_image = None

        self._camera = None

    def getPixelMultiplier(self):
        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(Application.getInstance().primaryScreen().physicalDotsPerInch() / 96.0)

    ##  Create a new material
    #   \param vert
    #   \param frag
    #   \return material
    def createMaterial(self, vert, frag):
        mat = QtGL2Material.QtGL2Material(self)
        mat.loadVertexShader(vert)
        mat.loadFragmentShader(frag)
        mat.build()
        return mat
    
    ##  Create frame buffer with given width/height
    #   \param width Width of buffer
    #   \param height Height of buffer
    #   \return Created frame buffer
    def createFrameBuffer(self, width, height):
        buffer_format = QOpenGLFramebufferObjectFormat()
        buffer_format.setAttachment(QOpenGLFramebufferObject.Depth)
        return QOpenGLFramebufferObject(width, height, buffer_format)

    ##  Set light position of global light
    def setLightPosition(self, position):
        self._light_position = position
    
    ##  Set background color of the rendering.
    def setBackgroundColor(self, color):
        self._background_color = color

    def setViewportSize(self, width, height):
        self._viewport_width = width
        self._viewport_height = height
        
    ##  Reset the selection image, so a redraw is forced.
    #   This is used when the scene is changed by delete actions, so the image needs to be redrawn.
    #   It can happen that mouse events fire much faster than rendering, which can cause problems 
    #   if the old selection image is still used.
    def resetSelectionImage(self):
        self._selection_image = None
    
    ##  Get the selection colors within a radius.
    #   All objects (either full objects or single points in a cloud are drawn with an unique color.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    #   \param radius Radius in pixels to select.
    #   \return list of colors ARGB (values from 0 to 1.)
    def getSelectionColorAtCoorindateRadius(self,x,y,radius):
        if not self._selection_image:
            return None
        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height
        squared_radius = radius * radius
        samples = []
        for sx in range(-radius, radius):
            squared_sx = sx*sx
            if px + sx < 0 or px + sx > (self._selection_image.width() - 1):
                continue
            for sy in range(-radius, radius):
                squared_sy = sy * sy
                if py + sy < 0 or py + sy > (self._selection_image.height() - 1):
                    continue
                if squared_sx + squared_sy < squared_radius:
                    pixel = self._selection_image.pixel(px + sx, py + sy)
                    samples.append(Color.fromARGB(pixel))
        return samples
    
    ##  Get the selection colors on coordinate
    #   All objects (either full objects or single points in a cloud are drawn with an unique color.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    #   \return color ARGB (values from 0 to 1.)
    def getSelectionColorAtCoordinate(self,x,y):
        if not self._selection_image:
            return None
        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height
        return Color.fromARGB(self._selection_image.pixel(px,py))
    
    ##  Get object ID at coordinate. 
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    def getIdAtCoordinate(self, x, y):
        if not self._selection_image:
            return None

        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height

        if px < 0 or px > (self._selection_image.width() - 1) or py < 0 or py > (self._selection_image.height() - 1):
            return None

        pixel = self._selection_image.pixel(px, py)
        return self._selection_map.get(Color.fromARGB(pixel), None)

    ##  Render selection is used to 'highlight' the selected objects
    def setRenderSelection(self, render):
        self._render_selection = render

    def beginRendering(self):
        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(), self._background_color.greenF(), self._background_color.blueF(), self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)

        if not QOpenGLContext.currentContext().format().renderableType() == QSurfaceFormat.OpenGLES:
            self._gl.glPointSize(2)

        self._solids_queue.clear()
        self._transparent_queue.clear()
        self._overlay_queue.clear()

        self._render_selection = True
   
    ##  Put a node in the render queue
    def queueNode(self, node, **kwargs):
        queue_item = { "node": node }

        if "mesh" in kwargs:
            queue_item["mesh"] = kwargs["mesh"]

        queue_item["material"] = kwargs.get("material", self._default_material)

        mode = kwargs.get("mode", Renderer.RenderTriangles)
        if mode is Renderer.RenderLines:
            queue_item["mode"] = self._gl.GL_LINES
        elif mode is Renderer.RenderLineLoop:
            queue_item["mode"] = self._gl.GL_LINE_LOOP
        elif mode is Renderer.RenderPoints:
            queue_item["mode"] = self._gl.GL_POINTS
        else:
            queue_item["mode"] = self._gl.GL_TRIANGLES

        queue_item["wireframe"] = (mode == Renderer.RenderWireframe)

        queue_item["force_single_sided"] = kwargs.get("force_single_sided", False)

        if kwargs.get("end", None):
            queue_item["range"] = [kwargs.get("start", 0), kwargs.get("end")]

        if kwargs.get("transparent", False):
            self._transparent_queue.append(queue_item)
        elif kwargs.get("overlay", False):
            self._overlay_queue.append(queue_item)
        else:
            self._solids_queue.append(queue_item)
    
    ##  Render all nodes in the queue
    def renderQueuedNodes(self):
        self._gl.glEnable(self._gl.GL_DEPTH_TEST)
        self._gl.glDepthFunc(self._gl.GL_LESS)
        self._gl.glDepthMask(self._gl.GL_TRUE)
        self._gl.glDisable(self._gl.GL_CULL_FACE)
        self._gl.glLineWidth(self.getPixelMultiplier())

        self._scene.acquireLock()

        self._camera = self._scene.getActiveCamera()
        if not self._camera:
            Logger.log("e", "No active camera set, can not render")
            self._scene.releaseLock()
            return

        # Render the selection image
        selectable_nodes = []
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.isSelectable() and node.getMeshData():
                selectable_nodes.append(node)
        if selectable_nodes:
            #TODO: Use a limited area around the mouse rather than a full viewport for rendering
            if self._selection_buffer.width() < self._viewport_width or self._selection_buffer.height() < self._viewport_height:
                self._selection_buffer = self.createFrameBuffer(self._viewport_width, self._viewport_height)

            self._selection_buffer.bind()
            self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)
            self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
            self._gl.glDisable(self._gl.GL_BLEND)
            self._selection_map.clear()
            for node in selectable_nodes:
                if type(node) is PointCloudNode: #Pointcloud node sets vertex color (to be used for point precise selection)
                    self._renderItem({
                        "node": node,
                        "material": self._handle_material,
                        "mode": self._gl.GL_POINTS
                    })
                else :
                    color = self._getObjectColor(node)
                    self._selection_map[color] = id(node)
                    self._selection_material.setUniformValue("u_color", color)
                    self._renderItem({
                        "node": node,
                        "material": self._selection_material,
                        "mode": self._gl.GL_TRIANGLES
                    })
            tool = self._controller.getActiveTool()
            if tool:
                tool_handle = tool.getHandle()
                if tool_handle and tool_handle.getSelectionMesh() and tool_handle.getParent():
                    self._selection_map.update(tool_handle.getSelectionMap())
                    self._gl.glDisable(self._gl.GL_DEPTH_TEST)
                    self._renderItem({
                        "node": tool_handle,
                        "mesh": tool_handle.getSelectionMesh(),
                        "material": self._handle_material,
                        "mode": self._gl.GL_TRIANGLES
                    })
                    self._gl.glEnable(self._gl.GL_DEPTH_TEST)

            self._selection_buffer.release()
            self._selection_image = self._selection_buffer.toImage()

        # Workaround for a MacOSX Intel HD Graphics 6000 Bug
        # Apparently, performing a glReadPixels call on a bound framebuffer breaks releasing the
        # depth buffer, which means the rest of the depth tested geometry never renders. To work-
        # around this we perform a clear here. Note that for some reason we also need to set
        # glClearColor since it is apparently not stored properly.
        self._gl.glClearColor(self._background_color.redF(), self._background_color.greenF(), self._background_color.blueF(), self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)

        self._gl.glEnable(self._gl.GL_STENCIL_TEST)
        self._gl.glStencilMask(0xff)
        self._gl.glClearStencil(0)
        self._gl.glClear(self._gl.GL_STENCIL_BUFFER_BIT)
        self._gl.glStencilFunc(self._gl.GL_ALWAYS, 0xff, 0xff)
        self._gl.glStencilOp(self._gl.GL_REPLACE, self._gl.GL_REPLACE, self._gl.GL_REPLACE)
        self._gl.glStencilMask(0)

        for item in self._solids_queue:
            if Selection.isSelected(item["node"]):
                self._gl.glStencilMask(0xff)
                self._renderItem(item)
                self._gl.glStencilMask(0)
            else:
                self._renderItem(item)

        if self._render_selection:
            self._gl.glStencilMask(0)
            self._gl.glStencilFunc(self._gl.GL_EQUAL, 0, 0xff)
            self._gl.glLineWidth(2 * self.getPixelMultiplier())
            for node in Selection.getAllSelectedObjects():
                if node.getMeshData() and type(node) is not PointCloudNode:
                    self._renderItem({
                        "node": node,
                        "material": self._outline_material,
                        "mode": self._gl.GL_TRIANGLES,
                        "wireframe": True
                    })

            self._gl.glLineWidth(self.getPixelMultiplier())

        self._gl.glDisable(self._gl.GL_STENCIL_TEST)
        self._gl.glDepthMask(self._gl.GL_FALSE)
        self._gl.glEnable(self._gl.GL_BLEND)
        self._gl.glEnable(self._gl.GL_CULL_FACE)
        self._gl.glBlendFunc(self._gl.GL_SRC_ALPHA, self._gl.GL_ONE_MINUS_SRC_ALPHA)

        for item in self._transparent_queue:
            self._renderItem(item)

        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_CULL_FACE)

        for item in self._overlay_queue:
            self._renderItem(item)

        self._scene.releaseLock()

    def endRendering(self):
        pass

    def _initialize(self):
        profile = QOpenGLVersionProfile()
        profile.setVersion(2, 0)
        self._gl = QOpenGLContext.currentContext().versionFunctions(profile)
        self._gl.initializeOpenGLFunctions()

        self._default_material = self.createMaterial(
                                     Resources.getPath(Resources.Shaders, "default.vert"),
                                     Resources.getPath(Resources.Shaders, "default.frag")
                                )

        self._default_material.setUniformValue("u_ambientColor", Color(0.3, 0.3, 0.3, 1.0))
        self._default_material.setUniformValue("u_diffuseColor", Color(0.5, 0.5, 0.5, 1.0))
        self._default_material.setUniformValue("u_specularColor", Color(0.7, 0.7, 0.7, 1.0))
        self._default_material.setUniformValue("u_shininess", 20.)

        self._selection_buffer = self.createFrameBuffer(128, 128)
        self._selection_material = self.createMaterial(
                                        Resources.getPath(Resources.Shaders, "basic.vert"),
                                        Resources.getPath(Resources.Shaders, "color.frag")
                                   )

        self._handle_material = self.createMaterial(
                                     Resources.getPath(Resources.Shaders, "basic.vert"),
                                     Resources.getPath(Resources.Shaders, "vertexcolor.frag")
                                )

        self._outline_material = self.createMaterial(
                                      Resources.getPath(Resources.Shaders, "outline.vert"),
                                       Resources.getPath(Resources.Shaders, "outline.frag")
                                 )

        self._initialized = True

    def _renderItem(self, item):
        node = item["node"]
        mesh = item.get("mesh", node.getMeshData())
        if not mesh:
            return #Something went wrong, node has no mesh.
        transform = node.getWorldTransformation()
        material = item["material"]
        mode = item["mode"]
        wireframe = item.get("wireframe", False)
        range = item.get("range", None)

        culling_enabled = self._gl.glIsEnabled(self._gl.GL_CULL_FACE)
        if item.get("force_single_sided") and not culling_enabled:
            self._gl.glEnable(self._gl.GL_CULL_FACE)

        material.bind()
        material.setUniformValue("u_projectionMatrix", self._camera.getProjectionMatrix(), cache = False)
        material.setUniformValue("u_viewMatrix", self._camera.getWorldTransformation().getInverse(), cache = False)
        material.setUniformValue("u_viewPosition", self._camera.getWorldPosition(), cache = False)
        material.setUniformValue("u_modelMatrix", transform, cache = False)
        material.setUniformValue("u_lightPosition", self._camera.getWorldPosition() + Vector(0, 50, 0), cache = False)

        if mesh.hasNormals():
            normal_matrix = copy.deepcopy(transform)
            normal_matrix.setRow(3, [0, 0, 0, 1])
            normal_matrix.setColumn(3, [0, 0, 0, 1])
            normal_matrix = normal_matrix.getInverse().getTransposed()
            material.setUniformValue("u_normalMatrix", normal_matrix, cache = False)

        try:
            vertex_buffer = getattr(mesh, vertexBufferProperty)
        except AttributeError:
            vertex_buffer =  self._createVertexBuffer(mesh)
        vertex_buffer.bind()

        if mesh.hasIndices():
            try:
                index_buffer = getattr(mesh, indexBufferProperty)
            except AttributeError:
                index_buffer = self._createIndexBuffer(mesh)
            index_buffer.bind()

        material.enableAttribute("a_vertex", "vector3f", 0)
        offset = mesh.getVertexCount() * 3 * 4

        if mesh.hasNormals():
            material.enableAttribute("a_normal", "vector3f", offset)
            offset += mesh.getVertexCount() * 3 * 4

        if mesh.hasColors():
            material.enableAttribute("a_color", "vector4f", offset)
            offset += mesh.getVertexCount() * 4 * 4

        if mesh.hasUVCoordinates():
            material.enableAttribute("a_uvs", "vector2f", offset)
            offset += mesh.getVertexCount() * 2 * 4

        if wireframe and hasattr(self._gl, "glPolygonMode"):
            self._gl.glPolygonMode(self._gl.GL_FRONT_AND_BACK, self._gl.GL_LINE)

        if mesh.hasIndices():
            if range is None:
                if mode == self._gl.GL_TRIANGLES:
                    self._gl.glDrawElements(mode, mesh.getFaceCount() * 3 , self._gl.GL_UNSIGNED_INT, None)
                else:
                    self._gl.glDrawElements(mode, mesh.getFaceCount(), self._gl.GL_UNSIGNED_INT, None)
            else:
                if mode == self._gl.GL_TRIANGLES:
                    self._gl.glDrawRangeElements(mode, range[0], range[1], range[1] - range[0], self._gl.GL_UNSIGNED_INT, None)
                else:
                    self._gl.glDrawRangeElements(mode, range[0], range[1], range[1] - range[0], self._gl.GL_UNSIGNED_INT, None)
        else:
            self._gl.glDrawArrays(mode, 0, mesh.getVertexCount())

        if wireframe and hasattr(self._gl, "glPolygonMode"):
            self._gl.glPolygonMode(self._gl.GL_FRONT_AND_BACK, self._gl.GL_FILL)

        material.disableAttribute("a_vertex")
        material.disableAttribute("a_normal")
        material.disableAttribute("a_color")
        material.disableAttribute("a_uvs")
        vertex_buffer.release()

        if mesh.hasIndices():
            index_buffer.release()

        material.release()

        if item.get("force_single_sided") and not culling_enabled:
            self._gl.glDisable(self._gl.GL_CULL_FACE)

    def _createVertexBuffer(self, mesh):
        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()

        buffer_size = mesh.getVertexCount() * 3 * 4 # Vertex count * number of components * sizeof(float32)
        if mesh.hasNormals():
            buffer_size += mesh.getVertexCount() * 3 * 4 # Vertex count * number of components * sizeof(float32)
        if mesh.hasColors():
            buffer_size += mesh.getVertexCount() * 4 * 4 # Vertex count * number of components * sizeof(float32)
        if mesh.hasUVCoordinates():
            buffer_size += mesh.getVertexCount() * 2 * 4 # Vertex count * number of components * sizeof(float32)

        buffer.allocate(buffer_size)

        offset = 0
        vertices = mesh.getVerticesAsByteArray()
        if vertices is not None:
            buffer.write(0, vertices, len(vertices))
            offset += len(vertices)

        if mesh.hasNormals():
            normals = mesh.getNormalsAsByteArray()
            buffer.write(offset, normals, len(normals))
            offset += len(normals)

        if mesh.hasColors():
            colors = mesh.getColorsAsByteArray()
            buffer.write(offset, colors, len(colors))
            offset += len(colors)

        if mesh.hasUVCoordinates():
            uvs = mesh.getUVCoordinatesAsByteArray()
            buffer.write(offset, uvs, len(uvs))
            offset += len(uvs)

        buffer.release()

        setattr(mesh, vertexBufferProperty, buffer)
        return buffer

    def _createIndexBuffer(self, mesh):
        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

        data = mesh.getIndicesAsByteArray()
        buffer.allocate(data, len(data))
        buffer.release()

        setattr(mesh, indexBufferProperty, buffer)
        return buffer
    
    ##  Create object color based on ID of node.
    #   \param node Node to get color for
    #   \return Color
    def _getObjectColor(self, node):
        obj_id = id(node)
        r = (obj_id & 0xff000000) >> 24
        g = (obj_id & 0x00ff0000) >> 16
        b = (obj_id & 0x0000ff00) >> 8
        a = (obj_id & 0x000000ff) >> 0
        return Color(r, g, b, a)
Exemple #16
0
def export(configDictionary={},
           projectURL=str(),
           pagesLocationList=[],
           pageData=[]):
    path = Path(os.path.join(projectURL, configDictionary["exportLocation"]))
    exportPath = path / "EPUB-files"
    metaInf = exportPath / "META-INF"
    oebps = exportPath / "OEBPS"
    imagePath = oebps / "Images"
    # Don't write empty folders. Epubcheck doesn't like that.
    # stylesPath = oebps / "Styles"
    textPath = oebps / "Text"

    if exportPath.exists() is False:
        exportPath.mkdir()
        metaInf.mkdir()
        oebps.mkdir()
        imagePath.mkdir()
        # stylesPath.mkdir()
        textPath.mkdir()

    # Due the way EPUB verifies, the mimetype needs to be packaged in first.
    # Due the way zips are constructed, the only way to ensure that is to
    # Fill the zip as we go along...

    # Use the project name if there's no title to avoid sillyness with unnamed zipfiles.
    title = configDictionary["projectName"]
    if "title" in configDictionary.keys():
        title = str(configDictionary["title"]).replace(" ", "_")

    # Get the appropriate path.
    url = str(path / str(title + ".epub"))

    # Create a zip file.
    epubArchive = zipfile.ZipFile(url,
                                  mode="w",
                                  compression=zipfile.ZIP_STORED)

    mimetype = open(str(Path(exportPath / "mimetype")), mode="w")
    mimetype.write("application/epub+zip")
    mimetype.close()

    # Write to zip.
    epubArchive.write(Path(exportPath / "mimetype"), Path("mimetype"))

    container = QDomDocument()
    cRoot = container.createElement("container")
    cRoot.setAttribute("version", "1.0")
    cRoot.setAttribute("xmlns",
                       "urn:oasis:names:tc:opendocument:xmlns:container")
    container.appendChild(cRoot)
    rootFiles = container.createElement("rootfiles")
    rootfile = container.createElement("rootfile")
    rootfile.setAttribute("full-path", "OEBPS/content.opf")
    rootfile.setAttribute("media-type", "application/oebps-package+xml")
    rootFiles.appendChild(rootfile)
    cRoot.appendChild(rootFiles)

    containerFileName = str(Path(metaInf / "container.xml"))

    containerFile = open(containerFileName, 'w', newline="", encoding="utf-8")
    containerFile.write(container.toString(indent=2))
    containerFile.close()

    # Write to zip.
    epubArchive.write(containerFileName,
                      os.path.relpath(containerFileName, str(exportPath)))

    # copyimages to images
    pagesList = []
    if len(pagesLocationList) > 0:
        if "cover" in configDictionary.keys():
            coverNumber = configDictionary["pages"].index(
                configDictionary["cover"])
        else:
            coverNumber = 0
        for p in pagesLocationList:
            if os.path.exists(p):
                shutil.copy2(p, str(imagePath))
                filename = str(Path(imagePath / os.path.basename(p)))
                pagesList.append(filename)
                epubArchive.write(filename,
                                  os.path.relpath(filename, str(exportPath)))
        if len(pagesLocationList) >= coverNumber:
            coverpageurl = pagesList[coverNumber]
    else:
        print("CPMT: Couldn't find the location for the epub images.")
        return False

    # for each image, make an xhtml file

    htmlFiles = []
    listOfNavItems = {}
    listofSpreads = []
    regions = []
    for i in range(len(pagesList)):
        pageName = "Page" + str(i) + ".xhtml"
        doc = QDomDocument()
        html = doc.createElement("html")
        doc.appendChild(html)
        html.setAttribute("xmlns", "http://www.w3.org/1999/xhtml")
        html.setAttribute("xmlns:epub", "http://www.idpf.org/2007/ops")

        # The viewport is a prerequisite to get pre-paginated
        # layouts working. We'll make the layout the same size
        # as the image.

        head = doc.createElement("head")
        viewport = doc.createElement("meta")
        viewport.setAttribute("name", "viewport")

        img = QImage()
        img.load(pagesLocationList[i])
        w = img.width()
        h = img.height()

        widthHeight = "width=" + str(w) + ", height=" + str(h)

        viewport.setAttribute("content", widthHeight)
        head.appendChild(viewport)
        html.appendChild(head)

        # Here, we process the region navigation data to percentages
        # because we have access here to the width and height of the viewport.

        data = pageData[i]
        transform = data["transform"]
        for v in data["vector"]:
            pointsList = []
            dominantColor = QColor(Qt.white)
            listOfColors = []
            for point in v["boundingBox"]:
                offset = QPointF(transform["offsetX"], transform["offsetY"])
                pixelPoint = QPointF(point.x() * transform["resDiff"],
                                     point.y() * transform["resDiff"])
                newPoint = pixelPoint - offset
                x = max(0, min(w, int(newPoint.x() * transform["scaleWidth"])))
                y = max(0, min(h,
                               int(newPoint.y() * transform["scaleHeight"])))
                listOfColors.append(img.pixelColor(QPointF(x, y).toPoint()))
                pointsList.append(QPointF((x / w) * 100, (y / h) * 100))
            regionType = "panel"
            if "text" in v.keys():
                regionType = "text"
            if len(listOfColors) > 0:
                dominantColor = listOfColors[-1]
                listOfColors = listOfColors[:-1]
                for color in listOfColors:
                    dominantColor.setRedF(
                        0.5 * (dominantColor.redF() + color.redF()))
                    dominantColor.setGreenF(
                        0.5 * (dominantColor.greenF() + color.greenF()))
                    dominantColor.setBlueF(
                        0.5 * (dominantColor.blueF() + color.blueF()))
            region = {}
            bounds = QPolygonF(pointsList).boundingRect()
            region["points"] = bounds
            region["type"] = regionType
            region["page"] = str(Path(textPath / pageName))
            region["primaryColor"] = dominantColor.name()
            regions.append(region)

        # We can also figureout here whether the page can be seen as a table of contents entry.

        if "acbf_title" in data["keys"]:
            listOfNavItems[str(Path(textPath / pageName))] = data["title"]

        # Or spreads...

        if "epub_spread" in data["keys"]:
            listofSpreads.append(str(Path(textPath / pageName)))

        body = doc.createElement("body")

        img = doc.createElement("img")
        img.setAttribute("src", os.path.relpath(pagesList[i], str(textPath)))
        body.appendChild(img)

        html.appendChild(body)

        filename = str(Path(textPath / pageName))
        docFile = open(filename, 'w', newline="", encoding="utf-8")
        docFile.write(doc.toString(indent=2))
        docFile.close()

        if pagesList[i] == coverpageurl:
            coverpagehtml = os.path.relpath(filename, str(oebps))
        htmlFiles.append(filename)

        # Write to zip.
        epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))

    # metadata

    filename = write_opf_file(oebps, configDictionary, htmlFiles, pagesList,
                              coverpageurl, coverpagehtml, listofSpreads)
    epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))

    filename = write_region_nav_file(oebps, configDictionary, htmlFiles,
                                     regions)
    epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))

    # toc
    filename = write_nav_file(oebps, configDictionary, htmlFiles,
                              listOfNavItems)
    epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))

    filename = write_ncx_file(oebps, configDictionary, htmlFiles,
                              listOfNavItems)
    epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))

    epubArchive.close()

    return True
Exemple #17
0
class QtRenderer(Renderer):
    def __init__(self):
        super().__init__()

        self._controller = Application.getInstance().getController()
        self._scene = self._controller.getScene()

        self._vertex_buffer_cache = {}
        self._index_buffer_cache = {}

        self._initialized = False

        self._light_position = Vector(0, 0, 0)
        self._background_color = QColor(128, 128, 128)
        self._viewport_width = 0
        self._viewport_height = 0
        self._window_width = 0
        self._window_height = 0

        self._batches = []

        self._quad_buffer = None

        self._camera = None

    initialized = Signal()

    ##  Get an integer multiplier that can be used to correct for screen DPI.
    def getPixelMultiplier(self):
        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(Application.getInstance().primaryScreen().physicalDotsPerInch() / 96.0)

    ##  Get the list of render batches.
    def getBatches(self):
        return self._batches

    ##  Overridden from Renderer.
    #
    #   This makes sure the added render pass has the right size.
    def addRenderPass(self, render_pass):
        super().addRenderPass(render_pass)
        render_pass.setSize(self._viewport_width, self._viewport_height)

    ##  Set background color used when rendering.
    def setBackgroundColor(self, color):
        self._background_color = color

    def getViewportWidth(self):
        return self._viewport_width

    def getViewportHeight(self):
        return self._viewport_height

    ##  Set the viewport size.
    #
    #   \param width The new width of the viewport.
    #   \param height The new height of the viewport.
    def setViewportSize(self, width, height):
        self._viewport_width = width
        self._viewport_height = height

        for render_pass in self._render_passes:
            render_pass.setSize(width, height)

    ##  Set the window size.
    def setWindowSize(self, width, height):
        self._window_width = width
        self._window_height = height

    ##  Get the window size.
    #
    #   \return A tuple of (window_width, window_height)
    def getWindowSize(self):
        return (self._window_width, self._window_height)

    ##  Overrides Renderer::beginRendering()
    def beginRendering(self):
        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(), self._background_color.greenF(), self._background_color.blueF(), self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
        self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)

    ##  Overrides Renderer::queueNode()
    def queueNode(self, node, **kwargs):
        type = kwargs.pop("type", RenderBatch.RenderType.Solid)
        if kwargs.pop("transparent", False):
            type = RenderBatch.RenderType.Transparent
        elif kwargs.pop("overlay", False):
            type = RenderBatch.RenderType.Overlay

        shader = kwargs.pop("shader", self._default_material)
        batch = RenderBatch(shader, type = type, **kwargs)

        batch.addItem(node.getWorldTransformation(), kwargs.get("mesh", node.getMeshData()), kwargs.pop("uniforms", None))

        self._batches.append(batch)

    ##  Overrides Renderer::render()
    def render(self):
        self._batches.sort()

        for render_pass in self.getRenderPasses():
            render_pass.render()

    ##  Overrides Renderer::endRendering()
    def endRendering(self):
        self._batches.clear()

    ##  Render a full screen quad (rectangle).
    #
    #   The function is used to draw render results on.
    #   \param shader The shader to use when rendering.
    def renderFullScreenQuad(self, shader):
        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_BLEND)

        shader.setUniformValue("u_modelViewProjectionMatrix", Matrix())

        if OpenGLContext.properties["supportsVertexArrayObjects"]:
            vao = QOpenGLVertexArrayObject()
            vao.create()
            vao.bind()

        self._quad_buffer.bind()

        shader.enableAttribute("a_vertex", "vector3f", 0)
        shader.enableAttribute("a_uvs", "vector2f", 72)

        self._gl.glDrawArrays(self._gl.GL_TRIANGLES, 0, 6)

        shader.disableAttribute("a_vertex")
        shader.disableAttribute("a_uvs")
        self._quad_buffer.release()

    def _initialize(self):
        supports_vao = OpenGLContext.supportsVertexArrayObjects()  # fill the OpenGLContext.properties
        Logger.log("d", "Support for Vertex Array Objects: %s", supports_vao)

        OpenGL.setInstance(OpenGL())
        self._gl = OpenGL.getInstance().getBindingsObject()

        self._default_material = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))

        self._render_passes.add(DefaultPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(SelectionPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(CompositePass(self._viewport_width, self._viewport_height))

        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()
        buffer.allocate(120)
        data = numpy.array([
            -1.0, -1.0, 0.0,
             1.0,  1.0, 0.0,
            -1.0,  1.0, 0.0,
            -1.0, -1.0, 0.0,
             1.0, -1.0, 0.0,
             1.0,  1.0, 0.0,
             0.0,  0.0,
             1.0,  1.0,
             0.0,  1.0,
             0.0,  0.0,
             1.0,  0.0,
             1.0,  1.0
        ], dtype = numpy.float32).tostring()
        buffer.write(0, data, len(data))
        buffer.release()
        self._quad_buffer = buffer

        self._initialized = True
        self.initialized.emit()
class GLWidget(QOpenGLWidget):

    clicked = pyqtSignal()

    PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2)

    vsrc = """
attribute highp vec4 vertex;
attribute mediump vec4 texCoord;
varying mediump vec4 texc;
uniform mediump mat4 matrix;
void main(void)
{
    gl_Position = matrix * vertex;
    texc = texCoord;
}
"""

    fsrc = """
uniform sampler2D texture;
varying mediump vec4 texc;
void main(void)
{
    gl_FragColor = texture2D(texture, texc.st);
}
"""

    coords = (((+1, -1, 0), (-1, -1, 0), (-1, +1, 0), (+1, +1, 0)),
              ((+1, 0, -1), (-1, 0, -1), (-1, 0, +1), (+1, 0, +1)),
              ((0, -1, +1), (0, -1, -1), (0, +1, -1), (0, +1, +1)),
              ((0, -1, -1), (0, -1, +1), (0, +1, +1),
               (0, +1, -1)), ((+1, 0, +1), (-1, 0, +1), (-1, 0, -1),
                              (+1, 0, -1)), ((-1, -1, 0), (+1, -1, 0),
                                             (+1, +1, 0), (-1, +1, 0)))

    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        print("Constructure")
        self.clearColor = QColor(Qt.black)
        self.xRot = 0
        self.yRot = 0
        self.zRot = 0
        self.program = 0

        self.lastPos = QPoint()

        self.textures = []
        self.texCoords = []
        self.vertices = []
        self.Isvol = False

        self.rate_x = 0.32  #self.s1/sum
        self.rate_y = 0.32  #self.s3/sum
        self.rate_z = 0.35  #self.s2/sum

        self.move_x = 0.0
        self.move_y = 0.0
        self.move_z = 0.0

        default_pix = 100
        _black = np.zeros((default_pix, default_pix, 3), dtype=np.uint8)

        # images

        ax_image = _black
        self.ax_image = ax_image.astype(np.uint8).copy()
        self.ax_image_flip = np.flipud(self.ax_image).copy()

        co_image = _black
        self.co_image = co_image.astype(np.uint8).copy()
        self.co_image_flip = np.fliplr(self.co_image).copy()

        sa_image = _black
        self.sa_image = sa_image.astype(np.uint8).copy()
        self.sa_image_flip = np.fliplr(self.sa_image).copy()

        # QImage

        self.axial_01 = QImage(self.ax_image, default_pix, default_pix,
                               3 * default_pix, QImage.Format_RGB888)
        self.axial_02 = QImage(self.ax_image_flip, default_pix, default_pix,
                               3 * default_pix, QImage.Format_RGB888)

        self.coronal_01 = QImage(self.co_image, default_pix, default_pix,
                                 3 * default_pix, QImage.Format_RGB888)
        self.coronal_02 = QImage(self.co_image_flip, default_pix, default_pix,
                                 3 * default_pix, QImage.Format_RGB888)

        self.sagittal_01 = QImage(self.sa_image, default_pix, default_pix,
                                  3 * default_pix, QImage.Format_RGB888)
        self.sagittal_02 = QImage(self.sa_image_flip, default_pix, default_pix,
                                  3 * default_pix, QImage.Format_RGB888)

    def axis_change(self):
        self.rate_x = 0.0
        self.rate_y = 0.0
        self.rate_z = 0.0
        self.makeObject()
        self.update()

    def Volume_change(self, volume_info=[]):
        print("Volume_change")
        ax_image, co_image, sa_image, ax_sz, co_sz, sa_sz = volume_info
        ax_image = ax_image.astype(np.uint8)
        co_image = co_image.astype(np.uint8)
        sa_image = sa_image.astype(np.uint8)

        # Size
        self.ax_sz = ax_sz
        self.co_sz = co_sz
        self.sa_sz = sa_sz

        # images

        #ax_image = self.brainmask_rgb[int(self.s3/2),:,:,:]
        ax_image = Draw_rect(ax_image, color=[0, 255, 0])
        self.ax_image = np.flipud(ax_image.astype(np.uint8)).copy()
        self.ax_image_flip = np.flipud(self.ax_image).copy()

        #co_image = self.brainmask_rgb[:, int(self.s2 / 2), :, :]
        co_image = Draw_rect(co_image, color=[255, 0, 0])
        self.co_image = co_image.astype(np.uint8).copy()
        self.co_image_flip = np.fliplr(self.co_image).copy()

        #sa_image = self.brainmask_rgb[:, :, int(self.s1/2), :]
        sa_image = Draw_rect(sa_image, color=[0, 0, 255])
        self.sa_image = sa_image.astype(np.uint8).copy()
        self.sa_image_flip = np.fliplr(self.sa_image).copy()

        # QImage
        # ax_sz, co_sz, sa_sz
        self.axial_01 = QImage(self.ax_image, sa_sz, co_sz, 3 * sa_sz,
                               QImage.Format_RGB888)
        self.axial_02 = QImage(self.ax_image_flip, sa_sz, co_sz, 3 * sa_sz,
                               QImage.Format_RGB888)

        self.coronal_01 = QImage(self.co_image, sa_sz, ax_sz, 3 * sa_sz,
                                 QImage.Format_RGB888)
        self.coronal_02 = QImage(self.co_image_flip, sa_sz, ax_sz, 3 * sa_sz,
                                 QImage.Format_RGB888)

        self.sagittal_01 = QImage(self.sa_image, co_sz, ax_sz, 3 * co_sz,
                                  QImage.Format_RGB888)
        self.sagittal_02 = QImage(self.sa_image_flip, co_sz, ax_sz, 3 * co_sz,
                                  QImage.Format_RGB888)

        # ax_sz, co_sz, sa_sz
        sum = ax_sz + co_sz + sa_sz
        # rate
        self.rate_x = co_sz / sum
        self.rate_y = ax_sz / sum
        self.rate_z = sa_sz / sum

        self.makeObject()

        self.update()

    def rotateBy(self, xAngle, yAngle, zAngle):
        self.xRot += xAngle
        self.yRot += yAngle
        self.zRot += zAngle
        self.update()

    def setClearColor(self, color):
        self.clearColor = color
        self.update()

    def load(self):
        # image change

        self.axial_01 = QImage(self.ax_image, self.s3, self.s2, 3 * self.s3,
                               QImage.Format_RGB888)
        self.axial_02 = QImage(self.ax_image_flip, self.s3, self.s2,
                               3 * self.s3, QImage.Format_RGB888)

        self.coronal_01 = QImage(
            self.ax_image, self.s3, self.s2, 3 * self.s3, QImage.Format_RGB888
        )  #QImage(self.co_image, self.s3, self.s1, 3 * self.s3, QImage.Format_RGB888)
        self.coronal_02 = QImage(
            self.ax_image_flip, self.s3, self.s2, 3 * self.s3,
            QImage.Format_RGB888
        )  #QImage(self.co_image_flip, self.s3, self.s1, 3 * self.s3, QImage.Format_RGB888)

        self.sagittal_01 = QImage(
            self.ax_image_flip, self.s3, self.s2, 3 * self.s3,
            QImage.Format_RGB888
        )  #QImage(self.sa_image, self.s2, self.s1, 3 * self.s2, QImage.Format_RGB888)
        self.sagittal_02 = QImage(
            self.ax_image_flip, self.s3, self.s2, 3 * self.s3,
            QImage.Format_RGB888
        )  #QImage(self.sa_image_flip, self.s2, self.s1, 3 * self.s2, QImage.Format_RGB888)

    def initializeGL(self):
        print("initializeGL")
        #self.gl = self.context().versionFunctions()
        #gl.initializeOpenGLFunctions()

        self.makeObject()

        gl.glEnable(gl.GL_DEPTH_TEST)
        gl.glEnable(gl.GL_CULL_FACE)
        gl.glEnable(gl.GL_TEXTURE_2D)
        vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
        vshader.compileSourceCode(self.vsrc)

        fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
        fshader.compileSourceCode(self.fsrc)
        #self.program.
        self.program = QOpenGLShaderProgram()
        self.program.addShader(vshader)
        self.program.addShader(fshader)
        self.program.bindAttributeLocation('vertex',
                                           self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.bindAttributeLocation('texCoord',
                                           self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.link()

        self.program.bind()
        self.program.setUniformValue('texture', 0)

        self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE,
                                       self.vertices)
        self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE,
                                       self.texCoords)

    def paintGL(self):
        gl.glClearColor(self.clearColor.redF(), self.clearColor.greenF(),
                        self.clearColor.blueF(), self.clearColor.alphaF())
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)

        m = QMatrix4x4()
        m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0)
        m.translate(0.0, 0.0, -10.0)
        m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0)
        m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0)
        m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0)

        camMatrix = QMatrix4x4(m)

        for i, texture in enumerate(self.textures):
            self.program.setUniformValue('matrix', camMatrix)

            if i == 0:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(0.0, 0.0, self.move_z)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 1:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(0.0, 0.0, self.move_z)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 4:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(0.0, self.move_y, 0.0)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 5:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(0.0, self.move_y, 0.0)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 3:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(self.move_x, 0.0, 0.0)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 2:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(self.move_x, 0.0, 0.0)
                self.program.setUniformValue('matrix', localMatrix)

            texture.bind()
            gl.glDrawArrays(gl.GL_TRIANGLE_FAN, i * 4, 4)

    def move_axial(self, volume_info, index):
        ax_image = volume_info
        ax_image = ax_image.astype(np.uint8)
        # images

        # co_image = self.brainmask_rgb[:, int(self.s2 / 2), :, :]
        ax_image = Draw_rect(ax_image, color=[0, 255, 0])
        self.ax_image = np.flipud(ax_image).astype(np.uint8).copy()
        self.ax_image_flip = np.flipud(self.ax_image).copy()

        # QImage
        # ax_sz, co_sz, sa_sz
        w, h, c = np.shape(self.ax_image)
        self.axial_01 = QImage(self.ax_image, h, w, 3 * h,
                               QImage.Format_RGB888)
        self.axial_02 = QImage(self.ax_image_flip, h, w, 3 * h,
                               QImage.Format_RGB888)

        self.move_y = (float(index) / float(self.ax_sz) -
                       0.5) * 2 * self.rate_y
        #print(self.move_y)

        self.makeObject()
        self.update()

    def move_sagittal(self, volume_info, index):
        sa_image = volume_info
        sa_image = sa_image.astype(np.uint8)
        # images

        # co_image = self.brainmask_rgb[:, int(self.s2 / 2), :, :]
        sa_image = Draw_rect(sa_image, color=[0, 0, 255])
        self.sa_image = sa_image.astype(np.uint8).copy()
        self.sa_image_flip = np.fliplr(self.sa_image).copy()
        #self.sa_image_flip = sa_image.astype(np.uint8).copy()
        # QImage
        # ax_sz, co_sz, sa_sz
        w, h, c = np.shape(self.sa_image)
        self.sagittal_01 = QImage(self.sa_image, h, w, 3 * h,
                                  QImage.Format_RGB888)
        self.sagittal_02 = QImage(self.sa_image_flip, h, w, 3 * h,
                                  QImage.Format_RGB888)

        self.move_x = (float(index) / float(self.sa_sz) -
                       0.5) * 2 * self.rate_x
        #print(self.move_x)

        self.makeObject()
        self.update()

    def move_coronal(self, volume_info, index):
        co_image = volume_info
        co_image = co_image.astype(np.uint8)
        # images

        # co_image = self.brainmask_rgb[:, int(self.s2 / 2), :, :]
        co_image = Draw_rect(co_image, color=[255, 0, 0])
        self.co_image = co_image.astype(np.uint8).copy()
        self.co_image_flip = np.fliplr(self.co_image).copy()

        # QImage
        # ax_sz, co_sz, sa_sz
        w, h, c = np.shape(self.co_image)
        self.coronal_01 = QImage(self.co_image, h, w, 3 * h,
                                 QImage.Format_RGB888)
        self.coronal_02 = QImage(self.co_image_flip, h, w, 3 * h,
                                 QImage.Format_RGB888)

        self.move_z = -(float(index) / float(self.co_sz) -
                        0.5) * 2 * self.rate_z
        #print(self.move_z)

        self.makeObject()
        self.update()

    def minimumSizeHint(self):
        return QSize(50, 50)

    def sizeHint(self):
        return QSize(200, 200)

    def makeObject(self):
        '''
            coords = (
            (( +1, -1, 0 ), ( -1, -1, 0 ), ( -1, +1, 0 ), ( +1, +1, 0 )),
            (( +1, 0, -1 ), ( -1, 0, -1 ), ( -1, 0, +1 ), ( +1, 0, +1 )),
            (( 0, -1, +1 ), ( 0, -1, -1 ), ( 0, +1, -1 ), ( 0, +1, +1 )),
            (( 0, -1, -1 ), ( 0, -1, +1 ), ( 0, +1, +1 ), ( 0, +1, -1 )),
            (( +1, 0, +1 ), ( -1, 0, +1 ), ( -1, 0, -1 ), ( +1, 0, -1 )),
            (( -1, -1, 0 ), ( +1, -1, 0 ), ( +1, +1, 0 ), ( -1, +1, 0 ))
        )
       '''

        self.textures = []
        self.texCoords = []
        self.vertices = []

        self.textures.append(QOpenGLTexture(self.coronal_01.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[0][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.coronal_02.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[5][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.sagittal_01.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[3][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.sagittal_02.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[2][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.axial_01.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[1][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.axial_02.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[4][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

    def resizeGL(self, width, height):
        side = min(width, height)
        gl.glViewport((width - side) // 2, (height - side) // 2, side, side)

    def mousePressEvent(self, event):
        self.lastPos = event.pos()

    def mouseMoveEvent(self, event):
        dx = event.x() - self.lastPos.x()
        dy = event.y() - self.lastPos.y()

        if event.buttons() & Qt.LeftButton:
            self.rotateBy(8 * dy, 8 * dx, 0)
        elif event.buttons() & Qt.RightButton:
            self.rotateBy(8 * dy, 0, 8 * dx)

        self.lastPos = event.pos()

    def mouseReleaseEvent(self, event):
        self.clicked.emit()
    def currentChanged(self, selected, deselected):
        # Get selected item
        self.selected = selected
        self.deselected = deselected

        # Get translation object
        _ = self.app._tr

        # Clear existing settings
        self.win.clear_effect_controls()

        # Get animation details
        animation = self.get_animation_details()
        self.selected_template = animation.get("service")

        # In newer versions of Qt, setting the model invokes the currentChanged signal,
        # but the selection is -1. So, just do nothing here.
        if not self.selected_template:
            return

        # Assign a new unique id for each template selected
        self.generateUniqueFolder()

        # Loop through params
        for param in animation.get("params", []):
            log.info('Using parameter %s: %s' % (param["name"], param["title"]))

            # Is Hidden Param?
            if param["name"] == "start_frame" or param["name"] == "end_frame":
                # add value to dictionary
                self.params[param["name"]] = int(param["default"])

                # skip to next param without rendering the controls
                continue

            # Create Label
            widget = None
            label = QLabel()
            label.setText(_(param["title"]))
            label.setToolTip(_(param["title"]))

            if param["type"] == "spinner":
                # add value to dictionary
                self.params[param["name"]] = float(param["default"])

                # create spinner
                widget = QDoubleSpinBox()
                widget.setMinimum(float(param["min"]))
                widget.setMaximum(float(param["max"]))
                widget.setValue(float(param["default"]))
                widget.setSingleStep(0.01)
                widget.setToolTip(param["title"])
                widget.valueChanged.connect(functools.partial(self.spinner_value_changed, param))

            elif param["type"] == "text":
                # add value to dictionary
                self.params[param["name"]] = _(param["default"])

                # create spinner
                widget = QLineEdit()
                widget.setText(_(param["default"]))
                widget.textChanged.connect(functools.partial(self.text_value_changed, widget, param))

            elif param["type"] == "multiline":
                # add value to dictionary
                self.params[param["name"]] = _(param["default"])

                # create spinner
                widget = QPlainTextEdit()
                widget.setPlainText(_(param["default"]).replace("\\n", "\n"))
                widget.textChanged.connect(functools.partial(self.text_value_changed, widget, param))

            elif param["type"] == "dropdown":
                # add value to dictionary
                self.params[param["name"]] = param["default"]

                # create spinner
                widget = QComboBox()
                widget.currentIndexChanged.connect(functools.partial(self.dropdown_index_changed, widget, param))

                # Add values to dropdown
                if "project_files" in param["name"]:
                    # override files dropdown
                    param["values"] = {}
                    for file in File.filter():
                        if file.data["media_type"] not in ("image", "video"):
                            continue

                        fileName = os.path.basename(file.data["path"])
                        fileExtension = os.path.splitext(fileName)[1]

                        if fileExtension.lower() in (".svg"):
                            continue

                        param["values"][fileName] = "|".join(
                            (file.data["path"],
                             str(file.data["height"]),
                             str(file.data["width"]),
                             file.data["media_type"],
                             str(file.data["fps"]["num"] / file.data["fps"]["den"])
                             )
                        )

                # Add normal values
                box_index = 0
                for k, v in sorted(param["values"].items()):
                    # add dropdown item
                    widget.addItem(_(k), v)

                    # select dropdown (if default)
                    if v == param["default"]:
                        widget.setCurrentIndex(box_index)
                    box_index = box_index + 1

                if not param["values"]:
                    widget.addItem(_("No Files Found"), "")
                    widget.setEnabled(False)

            elif param["type"] == "color":
                # add value to dictionary
                color = QColor(param["default"])
                if "diffuse_color" in param.get("name"):
                    self.params[param["name"]] = [color.redF(), color.greenF(), color.blueF(), color.alphaF()]
                else:
                    self.params[param["name"]] = [color.redF(), color.greenF(), color.blueF()]

                widget = QPushButton()
                widget.setText("")
                widget.setStyleSheet("background-color: {}".format(param["default"]))
                widget.clicked.connect(functools.partial(self.color_button_clicked, widget, param))

            # Add Label and Widget to the form
            if (widget and label):
                self.win.settingsContainer.layout().addRow(label, widget)
            elif (label):
                self.win.settingsContainer.layout().addRow(label)

        # Enable interface
        self.enable_interface()

        # Init slider values
        self.init_slider_values()
Exemple #20
0
class GLWidget(QOpenGLWidget):

    clicked = pyqtSignal()

    PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2)

    vsrc = """
attribute highp vec4 vertex;
attribute mediump vec4 texCoord;
varying mediump vec4 texc;
uniform mediump mat4 matrix;
void main(void)
{
    gl_Position = matrix * vertex;
    texc = texCoord;
}
"""

    fsrc = """
uniform sampler2D texture;
varying mediump vec4 texc;
void main(void)
{
    gl_FragColor = texture2D(texture, texc.st);
}
"""

    coords = (
        ((+1, -1, -1), (-1, -1, -1), (-1, +1, -1), (+1, +1, -1)),
        ((+1, +1, -1), (-1, +1, -1), (-1, +1, +1), (+1, +1, +1)),
        ((+1, -1, +1), (+1, -1, -1), (+1, +1, -1), (+1, +1, +1)),
        ((-1, -1, -1), (-1, -1, +1), (-1, +1, +1), (-1, +1, -1)),
        ((+1, -1, +1), (-1, -1, +1), (-1, -1, -1), (+1, -1, -1)),
        ((-1, -1, +1), (+1, -1, +1), (+1, +1, +1), (-1, +1, +1)),
    )

    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        self.clearColor = QColor(Qt.black)
        self.xRot = 0
        self.yRot = 0
        self.zRot = 0
        self.program = None

        self.lastPos = QPoint()

    def minimumSizeHint(self):
        return QSize(50, 50)

    def sizeHint(self):
        return QSize(200, 200)

    def rotateBy(self, xAngle, yAngle, zAngle):
        self.xRot += xAngle
        self.yRot += yAngle
        self.zRot += zAngle
        self.update()

    def setClearColor(self, color):
        self.clearColor = color
        self.update()

    def initializeGL(self):
        version_profile = QOpenGLVersionProfile()
        version_profile.setVersion(2, 0)
        self.gl = self.context().versionFunctions(version_profile)
        self.gl.initializeOpenGLFunctions()

        self.makeObject()

        self.gl.glEnable(self.gl.GL_DEPTH_TEST)
        self.gl.glEnable(self.gl.GL_CULL_FACE)

        vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
        vshader.compileSourceCode(self.vsrc)

        fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
        fshader.compileSourceCode(self.fsrc)

        self.program = QOpenGLShaderProgram()
        self.program.addShader(vshader)
        self.program.addShader(fshader)
        self.program.bindAttributeLocation("vertex",
                                           self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.bindAttributeLocation("texCoord",
                                           self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.link()

        self.program.bind()
        self.program.setUniformValue("texture", 0)

        self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE,
                                       self.vertices)
        self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE,
                                       self.texCoords)

    def paintGL(self):
        self.gl.glClearColor(
            self.clearColor.redF(),
            self.clearColor.greenF(),
            self.clearColor.blueF(),
            self.clearColor.alphaF(),
        )
        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT
                        | self.gl.GL_DEPTH_BUFFER_BIT)

        m = QMatrix4x4()
        m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0)
        m.translate(0.0, 0.0, -10.0)
        m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0)
        m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0)
        m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0)

        self.program.setUniformValue("matrix", m)

        for i, texture in enumerate(self.textures):
            texture.bind()
            self.gl.glDrawArrays(self.gl.GL_TRIANGLE_FAN, i * 4, 4)

    def resizeGL(self, width, height):
        side = min(width, height)
        self.gl.glViewport((width - side) // 2, (height - side) // 2, side,
                           side)

    def mousePressEvent(self, event):
        self.lastPos = event.pos()

    def mouseMoveEvent(self, event):
        dx = event.x() - self.lastPos.x()
        dy = event.y() - self.lastPos.y()

        if event.buttons() & Qt.LeftButton:
            self.rotateBy(8 * dy, 8 * dx, 0)
        elif event.buttons() & Qt.RightButton:
            self.rotateBy(8 * dy, 0, 8 * dx)

        self.lastPos = event.pos()

    def mouseReleaseEvent(self, event):
        self.clicked.emit()

    def makeObject(self):
        self.textures = []
        self.texCoords = []
        self.vertices = []

        root = QFileInfo(__file__).absolutePath()

        for i in range(6):
            self.textures.append(
                QOpenGLTexture(
                    QImage(root + ("/images/side%d.png" %
                                   (i + 1))).mirrored()))

            for j in range(4):
                self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))

                x, y, z = self.coords[i][j]
                self.vertices.append((0.2 * x, 0.2 * y, 0.2 * z))