camenduru's picture
thanks to vye16 ❤
fb5159d
"""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
# Note: these are the same definitions as for GLES, so we are not cross-polluting
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() # should expicitly set to 0s
@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,
}