Skip to main content
`Ctrl`+`K`

# Source code for silx.gui._glutils.utils

    # /*##########################################################################
    #
    # Copyright (c) 2014-2021 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 conversion functions between OpenGL and numpy types.
    """

    __authors__ = ["T. Vincent"]
    __license__ = "MIT"
    __date__ = "10/01/2017"

    import numpy

    from OpenGL.constants import BYTE_SIZES as _BYTE_SIZES
    from OpenGL.constants import ARRAY_TO_GL_TYPE_MAPPING as _ARRAY_TO_GL_TYPE_MAPPING

    def sizeofGLType(type_):
        """Returns the size in bytes of an element of type `type_`"""
        return _BYTE_SIZES[type_]

    def isSupportedGLType(type_):
        """Test if a numpy type or dtype can be converted to a GL type."""
        return numpy.dtype(type_).char in _ARRAY_TO_GL_TYPE_MAPPING

    def numpyToGLType(type_):
        """Returns the GL type corresponding the provided numpy type or dtype."""
        return _ARRAY_TO_GL_TYPE_MAPPING[numpy.dtype(type_).char]

    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.0, axis=1),  # All positive
            numpy.all(subVolumes <= 0.0, axis=1),
        )  # All negative
        intersect = numpy.where(intersect)[0]  # Indices of intersected triangles

        # Get barycentric coordinates
        with numpy.errstate(invalid="ignore"):
            barycentric = subVolumes[intersect] / volume[intersect].reshape(-1, 1)
        del subVolumes

        # Test segment/triangles intersection
        volAlpha = numpy.sum(t0s0CrossEdge01[intersect] * edge02[intersect], axis=1)
        with numpy.errstate(invalid="ignore"):
            t = volAlpha / volume[intersect]  # segment parameter of intersected triangles
        del t0s0CrossEdge01
        del edge02
        del volAlpha
        del volume

        inSegmentMask = numpy.logical_and(t >= 0.0, t <= 1.0)
        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]