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()
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]
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
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)
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()))
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))
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()
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)
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())
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)
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
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()
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))