| """Numpy (new version) module implementation of the OpenGL-ctypes array interfaces |
| |
| XXX Need to register handlers for all of the scalar types that numpy returns, |
| would like to have all return values be int/float if they are of compatible |
| type as well. |
| """ |
| REGISTRY_NAME = 'numpy' |
| import logging |
| from OpenGL import _configflags |
| _log = logging.getLogger( __name__ ) |
| try: |
| import numpy |
| except ImportError as err: |
| raise ImportError( """No numpy module present: %s"""%(err)) |
| import OpenGL |
| assert OpenGL |
| import ctypes |
| from OpenGL._bytes import long |
| from OpenGL.raw.GL import _types |
| from OpenGL.raw.GL.VERSION import GL_1_1 |
| from OpenGL import error |
| from OpenGL.arrays import formathandler |
| c_void_p = ctypes.c_void_p |
| from OpenGL import acceleratesupport |
| NumpyHandler = None |
| if acceleratesupport.ACCELERATE_AVAILABLE: |
| try: |
| from OpenGL_accelerate.numpy_formathandler import NumpyHandler |
| except ImportError as err: |
| _log.warning( |
| "Unable to load numpy_formathandler accelerator from OpenGL_accelerate" |
| ) |
| if NumpyHandler is None: |
| |
| testArray = numpy.array( [1,2,3,4],'i' ) |
| |
| |
| |
| if hasattr(testArray,'__array_interface__'): |
| def dataPointer( cls, instance ): |
| """Convert given instance to a data-pointer value (integer)""" |
| try: |
| return long(instance.__array_interface__['data'][0]) |
| except AttributeError: |
| instance = cls.asArray( instance ) |
| try: |
| return long(instance.__array_interface__['data'][0]) |
| except AttributeError: |
| return long(instance.__array_data__[0],0) |
| else: |
| def dataPointer( cls, instance ): |
| """Convert given instance to a data-pointer value (integer)""" |
| try: |
| return long(instance.__array_data__[0],0) |
| except AttributeError: |
| instance = cls.asArray( instance ) |
| try: |
| return long(instance.__array_interface__['data'][0]) |
| except AttributeError: |
| return long(instance.__array_data__[0],0) |
| try: |
| del testArray |
| except NameError as err: |
| pass |
| dataPointer = classmethod( dataPointer ) |
|
|
|
|
| class NumpyHandler( formathandler.FormatHandler ): |
| """Numpy-specific data-type handler for OpenGL |
| |
| Attributes: |
| |
| ERROR_ON_COPY -- if True, will raise errors |
| if we have to copy an array object in order to produce |
| a contiguous array of the correct type. |
| """ |
| HANDLED_TYPES = ( |
| numpy.ndarray, |
| numpy.bool_, |
| numpy.int8, |
| numpy.uint8, |
| numpy.int16, |
| numpy.uint16, |
| numpy.int32, |
| numpy.uint32, |
| numpy.int64, |
| numpy.uint64, |
| numpy.int64, |
| numpy.uint64, |
| numpy.float16, |
| numpy.float32, |
| numpy.float64, |
| numpy.complex64, |
| numpy.complex128, |
| numpy.bytes_, |
| numpy.str_, |
| numpy.void, |
| numpy.datetime64, |
| numpy.timedelta64, |
| ) |
| if hasattr(numpy,'float128'): |
| HANDLED_TYPES += (numpy.float128,) |
| if hasattr(numpy,'complex256'): |
| HANDLED_TYPES += (numpy.complex256,) |
| dataPointer = dataPointer |
| isOutput = True |
| ERROR_ON_COPY = _configflags.ERROR_ON_COPY |
| @classmethod |
| def zeros( cls, dims, typeCode ): |
| """Return Numpy array of zeros in given size""" |
| dims = numpy.array( dims, dtype='i') |
| return numpy.zeros( dims, GL_TYPE_TO_ARRAY_MAPPING[typeCode]) |
| @classmethod |
| def arrayToGLType( cls, value ): |
| """Given a value, guess OpenGL type of the corresponding pointer""" |
| typeCode = value.dtype |
| constant = ARRAY_TO_GL_TYPE_MAPPING.get( typeCode ) |
| if constant is None: |
| raise TypeError( |
| """Don't know GL type for array of type %r, known types: %s\nvalue:%s"""%( |
| typeCode, list(ARRAY_TO_GL_TYPE_MAPPING.keys()), value, |
| ) |
| ) |
| return constant |
|
|
| @classmethod |
| def arraySize( cls, value, typeCode = None ): |
| """Given a data-value, calculate dimensions for the array""" |
| return value.size |
| @classmethod |
| def arrayByteCount( cls, value, typeCode = None ): |
| """Given a data-value, calculate number of bytes required to represent""" |
| try: |
| return value.nbytes |
| except AttributeError: |
| if cls.ERROR_ON_COPY: |
| raise error.CopyError( |
| """Non-numpy array passed to numpy arrayByteCount: %s""", |
| type(value), |
| ) |
| value = cls.asArray( value, typeCode ) |
| return value.nbytes |
| @classmethod |
| def asArray( cls, value, typeCode=None ): |
| """Convert given value to an array value of given typeCode""" |
| if value is None: |
| return value |
| else: |
| return cls.contiguous( value, typeCode ) |
|
|
| @classmethod |
| def contiguous( cls, source, typeCode=None ): |
| """Get contiguous array from source |
| |
| source -- numpy Python array (or compatible object) |
| for use as the data source. If this is not a contiguous |
| array of the given typeCode, a copy will be made, |
| otherwise will just be returned unchanged. |
| typeCode -- optional 1-character typeCode specifier for |
| the numpy.array function. |
| |
| All gl*Pointer calls should use contiguous arrays, as non- |
| contiguous arrays will be re-copied on every rendering pass. |
| Although this doesn't raise an error, it does tend to slow |
| down rendering. |
| """ |
| typeCode = GL_TYPE_TO_ARRAY_MAPPING[ typeCode ] |
| try: |
| contiguous = source.flags.contiguous |
| except AttributeError: |
| if typeCode: |
| return numpy.ascontiguousarray( source, typeCode ) |
| else: |
| return numpy.ascontiguousarray( source ) |
| else: |
| if contiguous and (typeCode is None or typeCode==source.dtype.char): |
| return source |
| elif (contiguous and cls.ERROR_ON_COPY): |
| from OpenGL import error |
| raise error.CopyError( |
| """Array of type %r passed, required array of type %r""", |
| source.dtype.char, typeCode, |
| ) |
| else: |
| |
| |
| |
| |
| |
| if cls.ERROR_ON_COPY: |
| from OpenGL import error |
| raise error.CopyError( |
| """Non-contiguous array passed""", |
| source, |
| ) |
| if typeCode is None: |
| typeCode = source.dtype.char |
| return numpy.ascontiguousarray( source, typeCode ) |
| @classmethod |
| def unitSize( cls, value, typeCode=None ): |
| """Determine unit size of an array (if possible)""" |
| return value.shape[-1] |
| @classmethod |
| def dimensions( cls, value, typeCode=None ): |
| """Determine dimensions of the passed array value (if possible)""" |
| return value.shape |
| @classmethod |
| def from_param( cls, instance, typeCode=None ): |
| try: |
| pointer = cls.dataPointer( instance ) |
| except TypeError: |
| array = cls.asArray( instance, typeCode ) |
| pp = cls.dataPointer( array ) |
| pp._temporary_array_ = (array,) |
| return pp |
| else: |
| if typeCode and instance.dtype != GL_TYPE_TO_ARRAY_MAPPING[ typeCode ]: |
| raise error.CopyError( |
| """Array of type %r passed, required array of type %r""", |
| instance.dtype.char, typeCode, |
| ) |
| return c_void_p( pointer ) |
|
|
| try: |
| numpy.array( [1], 's' ) |
| SHORT_TYPE = 's' |
| except TypeError as err: |
| SHORT_TYPE = 'h' |
| USHORT_TYPE = 'H' |
|
|
| def lookupDtype( char ): |
| return numpy.zeros( (1,), dtype=char ).dtype |
|
|
| ARRAY_TO_GL_TYPE_MAPPING = { |
| lookupDtype('d'): GL_1_1.GL_DOUBLE, |
| lookupDtype('f'): GL_1_1.GL_FLOAT, |
| lookupDtype('i'): GL_1_1.GL_INT, |
| lookupDtype(SHORT_TYPE): GL_1_1.GL_SHORT, |
| lookupDtype(USHORT_TYPE): GL_1_1.GL_UNSIGNED_SHORT, |
| lookupDtype('B'): GL_1_1.GL_UNSIGNED_BYTE, |
| lookupDtype('c'): GL_1_1.GL_UNSIGNED_BYTE, |
| lookupDtype('b'): GL_1_1.GL_BYTE, |
| lookupDtype('I'): GL_1_1.GL_UNSIGNED_INT, |
| |
| None: None, |
| } |
| GL_TYPE_TO_ARRAY_MAPPING = { |
| GL_1_1.GL_DOUBLE: lookupDtype('d'), |
| GL_1_1.GL_FLOAT:lookupDtype('f'), |
| GL_1_1.GL_INT: lookupDtype('i'), |
| GL_1_1.GL_BYTE: lookupDtype('b'), |
| GL_1_1.GL_SHORT: lookupDtype(SHORT_TYPE), |
| GL_1_1.GL_UNSIGNED_INT: lookupDtype('I'), |
| GL_1_1.GL_UNSIGNED_BYTE: lookupDtype('B'), |
| GL_1_1.GL_UNSIGNED_SHORT: lookupDtype(USHORT_TYPE), |
| _types.GL_VOID_P: lookupDtype('P'), |
| None: None, |
| } |
|
|