| | """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, |
| | } |
| |
|