diff options
Diffstat (limited to 'silx/gui/plot/backends/glutils')
-rw-r--r-- | silx/gui/plot/backends/glutils/GLPlotTriangles.py | 193 | ||||
-rw-r--r-- | silx/gui/plot/backends/glutils/GLText.py | 23 | ||||
-rw-r--r-- | silx/gui/plot/backends/glutils/GLTexture.py | 3 | ||||
-rw-r--r-- | silx/gui/plot/backends/glutils/__init__.py | 3 |
4 files changed, 211 insertions, 11 deletions
diff --git a/silx/gui/plot/backends/glutils/GLPlotTriangles.py b/silx/gui/plot/backends/glutils/GLPlotTriangles.py new file mode 100644 index 0000000..c756749 --- /dev/null +++ b/silx/gui/plot/backends/glutils/GLPlotTriangles.py @@ -0,0 +1,193 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2019 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################*/ +""" +This module provides a class to render a set of 2D triangles +""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "03/04/2017" + + +import ctypes + +import numpy + +from .....math.combo import min_max +from .... import _glutils as glutils +from ...._glutils import gl + + +class GLPlotTriangles(object): + """Handle rendering of a set of colored triangles""" + + _PROGRAM = glutils.Program( + vertexShader=""" + #version 120 + + uniform mat4 matrix; + attribute float xPos; + attribute float yPos; + attribute vec4 color; + + varying vec4 vColor; + + void main(void) { + gl_Position = matrix * vec4(xPos, yPos, 0.0, 1.0); + vColor = color; + } + """, + fragmentShader=""" + #version 120 + + uniform float alpha; + varying vec4 vColor; + + void main(void) { + gl_FragColor = vColor; + gl_FragColor.a *= alpha; + } + """, + attrib0='xPos') + + def __init__(self, x, y, color, triangles, alpha=1.): + """ + + :param numpy.ndarray x: X coordinates of triangle corners + :param numpy.ndarray y: Y coordinates of triangle corners + :param numpy.ndarray color: color for each point + :param numpy.ndarray triangles: (N, 3) array of indices of triangles + :param float alpha: Opacity in [0, 1] + """ + # Check and convert input data + x = numpy.ravel(numpy.array(x, dtype=numpy.float32)) + y = numpy.ravel(numpy.array(y, dtype=numpy.float32)) + color = numpy.array(color, copy=False) + # Cast to uint32 + triangles = numpy.array(triangles, copy=False, dtype=numpy.uint32) + + assert x.size == y.size + assert x.size == len(color) + assert color.ndim == 2 and color.shape[1] in (3, 4) + if numpy.issubdtype(color.dtype, numpy.floating): + color = numpy.array(color, dtype=numpy.float32, copy=False) + elif numpy.issubdtype(color.dtype, numpy.integer): + color = numpy.array(color, dtype=numpy.uint8, copy=False) + else: + raise ValueError('Unsupported color type') + assert triangles.ndim == 2 and triangles.shape[1] == 3 + + self.__x_y_color = x, y, color + self.xMin, self.xMax = min_max(x, finite=True) + self.yMin, self.yMax = min_max(y, finite=True) + self.__triangles = triangles + self.__alpha = numpy.clip(float(alpha), 0., 1.) + self.__vbos = None + self.__indicesVbo = None + self.__picking_triangles = None + + def pick(self, x, y): + """Perform picking + + :param float x: X coordinates in plot data frame + :param float y: Y coordinates in plot data frame + :return: List of picked data point indices + :rtype: numpy.ndarray + """ + if (x < self.xMin or x > self.xMax or + y < self.yMin or y > self.yMax): + return () + xPts, yPts = self.__x_y_color[:2] + if self.__picking_triangles is None: + self.__picking_triangles = numpy.zeros( + self.__triangles.shape + (3,), dtype=numpy.float32) + self.__picking_triangles[:, :, 0] = xPts[self.__triangles] + self.__picking_triangles[:, :, 1] = yPts[self.__triangles] + + segment = numpy.array(((x, y, -1), (x, y, 1)), dtype=numpy.float32) + # Picked triangle indices + indices = glutils.segmentTrianglesIntersection( + segment, self.__picking_triangles)[0] + # Point indices + indices = numpy.unique(numpy.ravel(self.__triangles[indices])) + + # Sorted from furthest to closest point + dists = (xPts[indices] - x) ** 2 + (yPts[indices] - y) ** 2 + indices = indices[numpy.flip(numpy.argsort(dists))] + + return tuple(indices) + + def discard(self): + """Release resources on the GPU""" + if self.__vbos is not None: + self.__vbos[0].vbo.discard() + self.__vbos = None + self.__indicesVbo.discard() + self.__indicesVbo = None + + def prepare(self): + """Allocate resources on the GPU""" + if self.__vbos is None: + self.__vbos = glutils.vertexBuffer(self.__x_y_color) + # Normalization is need for color + self.__vbos[-1].normalization = True + + if self.__indicesVbo is None: + self.__indicesVbo = glutils.VertexBuffer( + numpy.ravel(self.__triangles), + usage=gl.GL_STATIC_DRAW, + target=gl.GL_ELEMENT_ARRAY_BUFFER) + + def render(self, matrix, isXLog, isYLog): + """Perform rendering + + :param numpy.ndarray matrix: 4x4 transform matrix to use + :param bool isXLog: + :param bool isYLog: + """ + self.prepare() + + if self.__vbos is None or self.__indicesVbo is None: + return # Nothing to display + + self._PROGRAM.use() + + gl.glUniformMatrix4fv(self._PROGRAM.uniforms['matrix'], + 1, + gl.GL_TRUE, + matrix.astype(numpy.float32)) + + gl.glUniform1f(self._PROGRAM.uniforms['alpha'], self.__alpha) + + for index, name in enumerate(('xPos', 'yPos', 'color')): + attr = self._PROGRAM.attributes[name] + gl.glEnableVertexAttribArray(attr) + self.__vbos[index].setVertexAttrib(attr) + + with self.__indicesVbo: + gl.glDrawElements(gl.GL_TRIANGLES, + self.__triangles.size, + glutils.numpyToGLType(self.__triangles.dtype), + ctypes.c_void_p(0)) diff --git a/silx/gui/plot/backends/glutils/GLText.py b/silx/gui/plot/backends/glutils/GLText.py index 3d262bc..725c12c 100644 --- a/silx/gui/plot/backends/glutils/GLText.py +++ b/silx/gui/plot/backends/glutils/GLText.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2014-2018 European Synchrotron Radiation Facility +# Copyright (c) 2014-2019 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -33,9 +33,11 @@ __date__ = "03/04/2017" from collections import OrderedDict +import weakref + import numpy -from ...._glutils import font, gl, getGLContext, Program, Texture +from ...._glutils import font, gl, Context, Program, Texture from .GLSupport import mat4Translate @@ -128,7 +130,7 @@ class Text2D(object): attrib0='position') # Discard texture objects when removed from the cache - _textures = _Cache(callback=lambda key, value: value[0].discard()) + _textures = weakref.WeakKeyDictionary() """Cache already created textures""" _sizes = _Cache() @@ -159,15 +161,20 @@ class Text2D(object): self._rotate = numpy.radians(rotate) def _getTexture(self, text): - key = getGLContext(), text - - if key not in self._textures: + # Retrieve/initialize texture cache for current context + context = Context.getCurrent() + if context not in self._textures: + self._textures[context] = _Cache( + callback=lambda key, value: value[0].discard()) + textures = self._textures[context] + + if text not in textures: image, offset = font.rasterText(text, font.getDefaultFontFamily()) if text not in self._sizes: self._sizes[text] = image.shape[1], image.shape[0] - self._textures[key] = ( + textures[text] = ( Texture(gl.GL_RED, data=image, minFilter=gl.GL_NEAREST, @@ -176,7 +183,7 @@ class Text2D(object): gl.GL_CLAMP_TO_EDGE)), offset) - return self._textures[key] + return textures[text] @property def text(self): diff --git a/silx/gui/plot/backends/glutils/GLTexture.py b/silx/gui/plot/backends/glutils/GLTexture.py index 25dd9f1..118a36f 100644 --- a/silx/gui/plot/backends/glutils/GLTexture.py +++ b/silx/gui/plot/backends/glutils/GLTexture.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2014-2017 European Synchrotron Radiation Facility +# Copyright (c) 2014-2019 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -163,7 +163,6 @@ class Image(object): data[yOrig:yOrig+hData, xOrig:xOrig+wData], format_, - shape=(hData, wData), texUnit=texUnit, minFilter=self._MIN_FILTER, magFilter=self._MAG_FILTER, diff --git a/silx/gui/plot/backends/glutils/__init__.py b/silx/gui/plot/backends/glutils/__init__.py index 771de39..d58c084 100644 --- a/silx/gui/plot/backends/glutils/__init__.py +++ b/silx/gui/plot/backends/glutils/__init__.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2014-2017 European Synchrotron Radiation Facility +# Copyright (c) 2014-2019 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -39,6 +39,7 @@ _logger = logging.getLogger(__name__) from .GLPlotCurve import * # noqa from .GLPlotFrame import * # noqa from .GLPlotImage import * # noqa +from .GLPlotTriangles import GLPlotTriangles # noqa from .GLSupport import * # noqa from .GLText import * # noqa from .GLTexture import * # noqa |