| | """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 operator,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 |
| | import ctypes |
| | from OpenGL._bytes import long, integer_types |
| | from OpenGL.raw.GL import _types |
| | from OpenGL.raw.GL.VERSION import GL_1_1 |
| | from OpenGL import constant, 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.warn( |
| | "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 as err: |
| | instance = cls.asArray( instance ) |
| | try: |
| | return long(instance.__array_interface__['data'][0]) |
| | except AttributeError as err: |
| | 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 as err: |
| | instance = cls.asArray( instance ) |
| | try: |
| | return long(instance.__array_interface__['data'][0]) |
| | except AttributeError as err: |
| | 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,) |
| | 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""" |
| | 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 as err: |
| | 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 as err: |
| | 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 as err: |
| | 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, |
| | } |
| |
|