"""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:
_ = [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,
'f': _types.GLfloat,
'd': _types.GLdouble,
'i': _types.GLint,
'I': _types.GLuint,
'h': _types.GLshort,
'H': _types.GLushort,
'b': _types.GLbyte,
'B': _types.GLubyte,
's': _types.GLchar,
}