| """Lists/tuples as data-format for storage |
| |
| Note: |
| This implementation is *far* less efficient than using Numpy |
| to support lists/tuples, as the code here is all available in |
| C-level code there. This implementation is required to allow |
| for usage without numpy installed. |
| """ |
| REGISTRY_NAME = 'lists' |
| import ctypes, _ctypes |
| |
| from OpenGL.raw.GL import _types |
| from OpenGL.arrays import _arrayconstants as GL_1_1 |
| from OpenGL import constant, error |
| from OpenGL._configflags import ERROR_ON_COPY |
| from OpenGL.arrays import formathandler |
| from OpenGL._bytes import bytes,unicode,as_8_bit |
| HANDLED_TYPES = (list,tuple) |
| import operator |
|
|
| def err_on_copy( func ): |
| """Decorator which raises informative error if we try to copy while ERROR_ON_COPY""" |
| if not ERROR_ON_COPY: |
| return func |
| else: |
| def raiseErrorOnCopy( self, value, *args, **named ): |
| raise error.CopyError( |
| """%s passed, cannot copy with ERROR_ON_COPY set, please use an array type which has native data-pointer support (e.g. numpy or ctypes arrays)"""%( value.__class__.__name__, ) |
| ) |
| raiseErrorOnCopy.__name__ = getattr(func,'__name__','raiseErrorOnCopy') |
| return raiseErrorOnCopy |
|
|
| class ListHandler( formathandler.FormatHandler ): |
| """Storage of array data in Python lists/arrays |
| |
| This mechanism, unlike multi-dimensional arrays, is not necessarily |
| uniform in type or dimension, so we have to do a lot of extra checks |
| to make sure that we get a correctly-structured array. That, as |
| well as the need to copy the arrays in Python code, makes this a far |
| less efficient implementation than the numpy implementation, which |
| does all the same things, but does them all in C code. |
| |
| Note: as an *output* format, this format handler produces ctypes |
| arrays, not Python lists, this is done for convenience in coding |
| the implementation, mostly. |
| """ |
| @err_on_copy |
| def from_param( self, instance, typeCode=None ): |
| try: |
| return ctypes.byref( instance ) |
| except (TypeError,AttributeError) as err: |
| array = self.asArray( instance, typeCode ) |
| pp = ctypes.c_void_p( ctypes.addressof( array ) ) |
| pp._temporary_array_ = (array,) |
| return pp |
| dataPointer = staticmethod( ctypes.addressof ) |
| HANDLED_TYPES = HANDLED_TYPES |
| isOutput = True |
| @err_on_copy |
| @classmethod |
| def voidDataPointer( cls, value ): |
| """Given value in a known data-pointer type, return void_p for pointer""" |
| return ctypes.byref( value ) |
| @classmethod |
| def zeros( cls, dims, typeCode ): |
| """Return array of zeros in given size""" |
| type = GL_TYPE_TO_ARRAY_MAPPING[ typeCode ] |
| for dim in dims: |
| type *= dim |
| return type() |
| @classmethod |
| def dimsOf( cls, x ): |
| """Calculate total dimension-set of the elements in x |
| |
| This is *extremely* messy, as it has to track nested arrays |
| where the arrays could be different sizes on all sorts of |
| levels... |
| """ |
| try: |
| dimensions = [ len(x) ] |
| except (TypeError,AttributeError,ValueError) as err: |
| return [] |
| else: |
| childDimension = None |
| for child in x: |
| newDimension = cls.dimsOf( child ) |
| if childDimension is not None: |
| if newDimension != childDimension: |
| raise ValueError( |
| """Non-uniform array encountered: %s versus %s"""%( |
| newDimension, childDimension, |
| ), x |
| ) |
|
|
| @classmethod |
| def arrayToGLType( cls, value ): |
| """Given a value, guess OpenGL type of the corresponding pointer""" |
|
|
| result = ARRAY_TO_GL_TYPE_MAPPING.get( value._type_ ) |
| if result is not None: |
| return result |
| raise TypeError( |
| """Don't know GL type for array of type %r, known types: %s\nvalue:%s"""%( |
| value._type_, list(ARRAY_TO_GL_TYPE_MAPPING.keys()), value, |
| ) |
| ) |
| @classmethod |
| def arraySize( cls, value, typeCode = None ): |
| """Given a data-value, calculate dimensions for the array""" |
| dims = 1 |
| for base in cls.types( value ): |
| length = getattr( base, '_length_', None) |
| if length is not None: |
| dims *= length |
| return dims |
| @classmethod |
| def types( cls, value ): |
| """Produce iterable producing all composite types""" |
| dimObject = value |
| while dimObject is not None: |
| yield dimObject |
| dimObject = getattr( dimObject, '_type_', None ) |
| if isinstance( dimObject, (bytes,unicode)): |
| dimObject = None |
| @classmethod |
| def dims( cls, value ): |
| """Produce iterable of all dimensions""" |
| for base in cls.types( value ): |
| length = getattr( base, '_length_', None) |
| if length is not None: |
| yield length |
| @err_on_copy |
| @classmethod |
| def asArray( cls, value, typeCode=None ): |
| """Convert given value to a ctypes array value of given typeCode |
| |
| This does a *lot* of work just to get the data into the correct |
| format. It's not going to be anywhere near as fast as a numpy |
| or similar approach! |
| """ |
| if typeCode is None: |
| raise NotImplementedError( """Haven't implemented type-inference for lists yet""" ) |
| arrayType = GL_TYPE_TO_ARRAY_MAPPING[ typeCode ] |
| if isinstance( value, (list,tuple)): |
| subItems = [ |
| cls.asArray( item, typeCode ) |
| for item in value |
| ] |
| if subItems: |
| for dim in cls.dimensions( subItems[0] )[::-1]: |
| arrayType *= dim |
| arrayType *= len( subItems ) |
| result = arrayType() |
| result[:] = subItems |
| return result |
| else: |
| return arrayType( value ) |
| @err_on_copy |
| @classmethod |
| def unitSize( cls, value, typeCode=None ): |
| """Determine unit size of an array (if possible)""" |
| return tuple(cls.dims(value))[-1] |
| @err_on_copy |
| @classmethod |
| def dimensions( cls, value, typeCode=None ): |
| """Determine dimensions of the passed array value (if possible)""" |
| return tuple( cls.dims(value) ) |
| @classmethod |
| def arrayByteCount( cls, value, typeCode = None ): |
| """Given a data-value, calculate number of bytes required to represent""" |
| return ctypes.sizeof( value ) |
|
|
|
|
| ARRAY_TO_GL_TYPE_MAPPING = { |
| _types.GLdouble: GL_1_1.GL_DOUBLE, |
| _types.GLfloat: GL_1_1.GL_FLOAT, |
| _types.GLint: GL_1_1.GL_INT, |
| _types.GLuint: GL_1_1.GL_UNSIGNED_INT, |
| _types.GLshort: GL_1_1.GL_SHORT, |
| _types.GLushort: GL_1_1.GL_UNSIGNED_SHORT, |
| |
| _types.GLchar: GL_1_1.GL_CHAR, |
| _types.GLbyte: GL_1_1.GL_BYTE, |
| _types.GLubyte: GL_1_1.GL_UNSIGNED_BYTE, |
| } |
| GL_TYPE_TO_ARRAY_MAPPING = { |
| GL_1_1.GL_DOUBLE: _types.GLdouble, |
| GL_1_1.GL_FLOAT: _types.GLfloat, |
| GL_1_1.GL_INT: _types.GLint, |
| GL_1_1.GL_UNSIGNED_INT: _types.GLuint, |
| GL_1_1.GL_SHORT: _types.GLshort, |
| GL_1_1.GL_UNSIGNED_SHORT: _types.GLushort, |
| |
| GL_1_1.GL_CHAR: _types.GLchar, |
| GL_1_1.GL_BYTE: _types.GLbyte, |
| GL_1_1.GL_UNSIGNED_BYTE: _types.GLubyte, |
| } |
|
|