summaryrefslogtreecommitdiff
path: root/silx/gui/_glutils
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/_glutils')
-rw-r--r--silx/gui/_glutils/Context.py42
-rw-r--r--silx/gui/_glutils/Program.py12
-rw-r--r--silx/gui/_glutils/Texture.py10
-rw-r--r--silx/gui/_glutils/__init__.py5
-rw-r--r--silx/gui/_glutils/utils.py73
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]