diff options
Diffstat (limited to 'silx/gui/_glutils')
-rw-r--r-- | silx/gui/_glutils/Context.py | 42 | ||||
-rw-r--r-- | silx/gui/_glutils/Program.py | 12 | ||||
-rw-r--r-- | silx/gui/_glutils/Texture.py | 10 | ||||
-rw-r--r-- | silx/gui/_glutils/__init__.py | 5 | ||||
-rw-r--r-- | silx/gui/_glutils/utils.py | 73 |
5 files changed, 115 insertions, 27 deletions
diff --git a/silx/gui/_glutils/Context.py b/silx/gui/_glutils/Context.py index 7600992..c62dbb9 100644 --- a/silx/gui/_glutils/Context.py +++ b/silx/gui/_glutils/Context.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 @@ -32,32 +32,44 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" __date__ = "25/07/2016" +import contextlib -# context ##################################################################### +class _DEFAULT_CONTEXT(object): + """The default value for OpenGL context""" + pass -def _defaultGLContextGetter(): - return None +_context = _DEFAULT_CONTEXT +"""The current OpenGL context""" -_glContextGetter = _defaultGLContextGetter - -def getGLContext(): +def getCurrent(): """Returns platform dependent object of current OpenGL context. This is useful to associate OpenGL resources with the context they are created in. :return: Platform specific OpenGL context - :rtype: None by default or a platform dependent object""" - return _glContextGetter() + """ + return _context + + +def setCurrent(context=_DEFAULT_CONTEXT): + """Set a platform dependent OpenGL context + + :param context: Platform dependent GL context + """ + global _context + _context = context -def setGLContextGetter(getter=_defaultGLContextGetter): - """Set a platform dependent function to retrieve the current OpenGL context +@contextlib.contextmanager +def current(context): + """Context manager setting the platform-dependent GL context - :param getter: Platform dependent GL context getter - :type getter: Function with no args returning the current OpenGL context + :param context: Platform dependent GL context """ - global _glContextGetter - _glContextGetter = getter + previous_context = getCurrent() + setCurrent(context) + yield + setCurrent(previous_context) diff --git a/silx/gui/_glutils/Program.py b/silx/gui/_glutils/Program.py index 48c12f5..87eec5f 100644 --- a/silx/gui/_glutils/Program.py +++ b/silx/gui/_glutils/Program.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 @@ -30,11 +30,11 @@ __date__ = "25/07/2016" import logging +import weakref import numpy -from . import gl -from .Context import getGLContext +from . import Context, gl _logger = logging.getLogger(__name__) @@ -61,7 +61,7 @@ class Program(object): self._vertexShader = vertexShader self._fragmentShader = fragmentShader self._attrib0 = attrib0 - self._programs = {} + self._programs = weakref.WeakKeyDictionary() @staticmethod def _compileGL(vertexShader, fragmentShader, attrib0): @@ -106,7 +106,7 @@ class Program(object): return program, attributes, uniforms def _getProgramInfo(self): - glcontext = getGLContext() + glcontext = Context.getCurrent() if glcontext not in self._programs: raise RuntimeError( "Program was not compiled for current OpenGL context.") @@ -149,7 +149,7 @@ class Program(object): def use(self): """Make use of the program, compiling it if necessary""" - glcontext = getGLContext() + glcontext = Context.getCurrent() if glcontext not in self._programs: self._programs[glcontext] = self._compileGL( diff --git a/silx/gui/_glutils/Texture.py b/silx/gui/_glutils/Texture.py index 0875ebe..a7fd44b 100644 --- a/silx/gui/_glutils/Texture.py +++ b/silx/gui/_glutils/Texture.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 @@ -29,7 +29,11 @@ __license__ = "MIT" __date__ = "04/10/2016" -import collections +try: + from collections import abc +except ImportError: # Python2 support + import collections as abc + from ctypes import c_void_p import logging @@ -93,7 +97,7 @@ class Texture(object): self.magFilter = magFilter if magFilter is not None else gl.GL_LINEAR if wrap is not None: - if not isinstance(wrap, collections.Iterable): + if not isinstance(wrap, abc.Iterable): wrap = [wrap] * self.ndim assert len(wrap) == self.ndim diff --git a/silx/gui/_glutils/__init__.py b/silx/gui/_glutils/__init__.py index 15e48e1..e88affd 100644 --- a/silx/gui/_glutils/__init__.py +++ b/silx/gui/_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 @@ -34,9 +34,10 @@ __date__ = "25/07/2016" # OpenGL convenient functions from .OpenGLWidget import OpenGLWidget # noqa -from .Context import getGLContext, setGLContextGetter # noqa +from . import Context # noqa from .FramebufferTexture import FramebufferTexture # noqa from .Program import Program # noqa from .Texture import Texture # noqa from .VertexBuffer import VertexBuffer, VertexBufferAttrib, vertexBuffer # noqa from .utils import sizeofGLType, isSupportedGLType, numpyToGLType # noqa +from .utils import segmentTrianglesIntersection # noqa diff --git a/silx/gui/_glutils/utils.py b/silx/gui/_glutils/utils.py index 73af338..35cf819 100644 --- a/silx/gui/_glutils/utils.py +++ b/silx/gui/_glutils/utils.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 @@ -68,3 +68,74 @@ def isSupportedGLType(type_): def numpyToGLType(type_): """Returns the GL type corresponding the provided numpy type or dtype.""" return _TYPE_CONVERTER[numpy.dtype(type_)] + + +def segmentTrianglesIntersection(segment, triangles): + """Check for segment/triangles intersection. + + This is based on signed tetrahedron volume comparison. + + See A. Kensler, A., Shirley, P. + Optimizing Ray-Triangle Intersection via Automated Search. + Symposium on Interactive Ray Tracing, vol. 0, p33-38 (2006) + + :param numpy.ndarray segment: + Segment end points as a 2x3 array of coordinates + :param numpy.ndarray triangles: + Nx3x3 array of triangles + :return: (triangle indices, segment parameter, barycentric coord) + Indices of intersected triangles, "depth" along the segment + of the intersection point and barycentric coordinates of intersection + point in the triangle. + :rtype: List[numpy.ndarray] + """ + # TODO triangles from vertices + indices + # TODO early rejection? e.g., check segment bbox vs triangle bbox + segment = numpy.asarray(segment) + assert segment.ndim == 2 + assert segment.shape == (2, 3) + + triangles = numpy.asarray(triangles) + assert triangles.ndim == 3 + assert triangles.shape[1] == 3 + + # Test line/triangles intersection + d = segment[1] - segment[0] + t0s0 = segment[0] - triangles[:, 0, :] + edge01 = triangles[:, 1, :] - triangles[:, 0, :] + edge02 = triangles[:, 2, :] - triangles[:, 0, :] + + dCrossEdge02 = numpy.cross(d, edge02) + t0s0CrossEdge01 = numpy.cross(t0s0, edge01) + volume = numpy.sum(dCrossEdge02 * edge01, axis=1) + del edge01 + subVolumes = numpy.empty((len(triangles), 3), dtype=triangles.dtype) + subVolumes[:, 1] = numpy.sum(dCrossEdge02 * t0s0, axis=1) + del dCrossEdge02 + subVolumes[:, 2] = numpy.sum(t0s0CrossEdge01 * d, axis=1) + subVolumes[:, 0] = volume - subVolumes[:, 1] - subVolumes[:, 2] + intersect = numpy.logical_or( + numpy.all(subVolumes >= 0., axis=1), # All positive + numpy.all(subVolumes <= 0., axis=1)) # All negative + intersect = numpy.where(intersect)[0] # Indices of intersected triangles + + # Get barycentric coordinates + barycentric = subVolumes[intersect] / volume[intersect].reshape(-1, 1) + del subVolumes + + # Test segment/triangles intersection + volAlpha = numpy.sum(t0s0CrossEdge01[intersect] * edge02[intersect], axis=1) + t = volAlpha / volume[intersect] # segment parameter of intersected triangles + del t0s0CrossEdge01 + del edge02 + del volAlpha + del volume + + inSegmentMask = numpy.logical_and(t >= 0., t <= 1.) + intersect = intersect[inSegmentMask] + t = t[inSegmentMask] + barycentric = barycentric[inSegmentMask] + + # Sort intersecting triangles by t + indices = numpy.argsort(t) + return intersect[indices], t[indices], barycentric[indices] |