Module c3d.parameter
Classes used to represent the concept of a parameter in a .c3d file.
Expand source code
''' Classes used to represent the concept of a parameter in a .c3d file.
'''
import struct
import numpy as np
from .utils import DEC_to_IEEE, DEC_to_IEEE_BYTES
class ParamData(object):
'''A class representing a single named parameter from a C3D file.
Attributes
----------
name : str
Name of this parameter.
dtype: DataTypes
Reference to the DataTypes object associated with the file.
desc : str
Brief description of this parameter.
bytes_per_element : int, optional
For array data, this describes the size of each element of data. For
string data (including arrays of strings), this should be -1.
dimensions : list of int
For array data, this describes the dimensions of the array, stored in
column-major (Fortran) order. For arrays of strings, the dimensions here will be
the number of columns (length of each string) followed by the number of
rows (number of strings).
bytes : str
Raw data for this parameter.
'''
def __init__(self,
name,
dtype,
desc='',
bytes_per_element=1,
dimensions=None,
bytes=b'',
handle=None):
'''Set up a new parameter, only the name is required.'''
self.name = name
self.dtypes = dtype
self.desc = desc
self.bytes_per_element = bytes_per_element
self.dimensions = dimensions or []
self.bytes = bytes
if handle:
self.read(handle)
def __repr__(self):
return '<Param: {}>'.format(self.desc)
@property
def num_elements(self) -> int:
'''Return the number of elements in this parameter's array value.'''
e = 1
for d in self.dimensions:
e *= d
return e
@property
def total_bytes(self) -> int:
'''Return the number of bytes used for storing this parameter's data.'''
return self.num_elements * abs(self.bytes_per_element)
@property
def binary_size(self) -> int:
'''Return the number of bytes needed to store this parameter.'''
return (
1 + # group_id
2 + # next offset marker
1 + len(self.name.encode('utf-8')) + # size of name and name bytes
1 + # data size
# size of dimensions and dimension bytes
1 + len(self.dimensions) +
self.total_bytes + # data
1 + len(self.desc.encode('utf-8')) # size of desc and desc bytes
)
def write(self, group_id, handle):
'''Write binary data for this parameter to a file handle.
Parameters
----------
group_id : int
The numerical ID of the group that holds this parameter.
handle : file handle
An open, writable, binary file handle.
'''
name = self.name.encode('utf-8')
handle.write(struct.pack('bb', len(name), group_id))
handle.write(name)
handle.write(struct.pack('<h', self.binary_size - 2 - len(name)))
handle.write(struct.pack('b', self.bytes_per_element))
handle.write(struct.pack('B', len(self.dimensions)))
handle.write(struct.pack('B' * len(self.dimensions), *self.dimensions))
if self.bytes is not None and len(self.bytes) > 0:
handle.write(self.bytes)
desc = self.desc.encode('utf-8')
handle.write(struct.pack('B', len(desc)))
handle.write(desc)
def read(self, handle):
'''Read binary data for this parameter from a file handle.
This reads exactly enough data from the current position in the file to
initialize the parameter.
'''
self.bytes_per_element, = struct.unpack('b', handle.read(1))
dims, = struct.unpack('B', handle.read(1))
self.dimensions = [struct.unpack('B', handle.read(1))[
0] for _ in range(dims)]
self.bytes = b''
if self.total_bytes:
self.bytes = handle.read(self.total_bytes)
desc_size, = struct.unpack('B', handle.read(1))
self.desc = desc_size and self.dtypes.decode_string(handle.read(desc_size)) or ''
def _as(self, dtype):
'''Unpack the raw bytes of this param using the given struct format.'''
return np.frombuffer(self.bytes, count=1, dtype=dtype)[0]
def _as_array(self, dtype, copy=True):
'''Unpack the raw bytes of this param using the given data format.'''
if not self.dimensions:
return [self._as(dtype)]
elems = np.frombuffer(self.bytes, dtype=dtype)
# Reverse shape as the shape is defined in fortran format
view = elems.reshape(self.dimensions[::-1])
if copy:
return view.copy()
return view
class ParamReadonly(object):
''' Wrapper exposing readonly attributes of a `c3d.parameter.ParamData` entry.
'''
def __init__(self, data):
self._data = data
def __eq__(self, other):
return self._data is other._data
@property
def name(self) -> str:
''' Get the parameter name. '''
return self._data.name
@property
def desc(self) -> str:
''' Get the parameter descriptor. '''
return self._data.desc
@property
def dtypes(self):
''' Convenience accessor to the `c3d.dtypes.DataTypes` instance associated with the parameter. '''
return self._data.dtypes
@property
def dimensions(self) -> (int, ...):
''' Shape of the parameter data (Fortran format). '''
return self._data.dimensions
@property
def num_elements(self) -> int:
'''Return the number of elements in this parameter's array value.'''
return self._data.num_elements
@property
def bytes_per_element(self) -> int:
'''Return the number of bytes used to store each data element.'''
return self._data.bytes_per_element
@property
def total_bytes(self) -> int:
'''Return the number of bytes used for storing this parameter's data.'''
return self._data.total_bytes
@property
def binary_size(self) -> int:
'''Return the number of bytes needed to store this parameter.'''
return self._data.binary_size
@property
def int8_value(self):
'''Get the parameter data as an 8-bit signed integer.'''
return self._data._as(self.dtypes.int8)
@property
def uint8_value(self):
'''Get the parameter data as an 8-bit unsigned integer.'''
return self._data._as(self.dtypes.uint8)
@property
def int16_value(self):
'''Get the parameter data as a 16-bit signed integer.'''
return self._data._as(self.dtypes.int16)
@property
def uint16_value(self):
'''Get the parameter data as a 16-bit unsigned integer.'''
return self._data._as(self.dtypes.uint16)
@property
def int32_value(self):
'''Get the parameter data as a 32-bit signed integer.'''
return self._data._as(self.dtypes.int32)
@property
def uint32_value(self):
'''Get the parameter data as a 32-bit unsigned integer.'''
return self._data._as(self.dtypes.uint32)
@property
def uint_value(self):
''' Get the parameter data as a unsigned integer of appropriate type. '''
if self.bytes_per_element >= 4:
return self.uint32_value
elif self.bytes_per_element >= 2:
return self.uint16_value
else:
return self.uint8_value
@property
def int_value(self):
''' Get the parameter data as a signed integer of appropriate type. '''
if self.bytes_per_element >= 4:
return self.int32_value
elif self.bytes_per_element >= 2:
return self.int16_value
else:
return self.int8_value
@property
def float_value(self):
'''Get the parameter data as a floating point value of appropriate type.'''
if self.bytes_per_element > 4:
if self.dtypes.is_dec:
raise AttributeError("64 bit DEC floating point is not supported.")
# 64-bit floating point is not a standard
return self._data._as(self.dtypes.float64)
elif self.bytes_per_element == 4:
if self.dtypes.is_dec:
return DEC_to_IEEE(self._data._as(np.uint32))
else: # is_mips or is_ieee
return self._data._as(self.dtypes.float32)
else:
raise AttributeError("Only 32 and 64 bit floating point is supported.")
@property
def bytes_value(self) -> bytes:
'''Get the raw byte string.'''
return self._data.bytes
@property
def string_value(self):
'''Get the parameter data as a unicode string.'''
return self.dtypes.decode_string(self._data.bytes)
@property
def int8_array(self):
'''Get the parameter data as an array of 8-bit signed integers.'''
return self._data._as_array(self.dtypes.int8)
@property
def uint8_array(self):
'''Get the parameter data as an array of 8-bit unsigned integers.'''
return self._data._as_array(self.dtypes.uint8)
@property
def int16_array(self):
'''Get the parameter data as an array of 16-bit signed integers.'''
return self._data._as_array(self.dtypes.int16)
@property
def uint16_array(self):
'''Get the parameter data as an array of 16-bit unsigned integers.'''
return self._data._as_array(self.dtypes.uint16)
@property
def int32_array(self):
'''Get the parameter data as an array of 32-bit signed integers.'''
return self._data._as_array(self.dtypes.int32)
@property
def uint32_array(self):
'''Get the parameter data as an array of 32-bit unsigned integers.'''
return self._data._as_array(self.dtypes.uint32)
@property
def int64_array(self):
'''Get the parameter data as an array of 32-bit signed integers.'''
return self._data._as_array(self.dtypes.int64)
@property
def uint64_array(self):
'''Get the parameter data as an array of 32-bit unsigned integers.'''
return self._data._as_array(self.dtypes.uint64)
@property
def float32_array(self):
'''Get the parameter data as an array of 32-bit floats.'''
# Convert float data if not IEEE processor
if self.dtypes.is_dec:
# _as_array but for DEC
if not self.dimensions:
return [self.float_value]
return DEC_to_IEEE_BYTES(self._data.bytes).reshape(self.dimensions[::-1]) # Reverse fortran format
else: # is_ieee or is_mips
return self._data._as_array(self.dtypes.float32)
@property
def float64_array(self):
'''Get the parameter data as an array of 64-bit floats.'''
# Convert float data if not IEEE processor
if self.dtypes.is_dec:
raise ValueError('Unable to convert bytes encoded in a 64 bit floating point DEC format.')
else: # is_ieee or is_mips
return self._data._as_array(self.dtypes.float64)
@property
def float_array(self):
'''Get the parameter data as an array of 32 or 64 bit floats.'''
# Convert float data if not IEEE processor
if self.bytes_per_element == 4:
return self.float32_array
elif self.bytes_per_element == 8:
return self.float64_array
else:
raise TypeError("Parsing parameter bytes to an array with %i bit " % self.bytes_per_element +
"floating-point precission is not unsupported.")
@property
def int_array(self):
'''Get the parameter data as an array of integer values.'''
# Convert float data if not IEEE processor
if self.bytes_per_element == 1:
return self.int8_array
elif self.bytes_per_element == 2:
return self.int16_array
elif self.bytes_per_element == 4:
return self.int32_array
elif self.bytes_per_element == 8:
return self.int64_array
else:
raise TypeError("Parsing parameter bytes to an array with %i bit integer values is not unsupported." %
self.bytes_per_element)
@property
def uint_array(self):
'''Get the parameter data as an array of integer values.'''
# Convert float data if not IEEE processor
if self.bytes_per_element == 1:
return self.uint8_array
elif self.bytes_per_element == 2:
return self.uint16_array
elif self.bytes_per_element == 4:
return self.uint32_array
elif self.bytes_per_element == 8:
return self.uint64_array
else:
raise TypeError("Parsing parameter bytes to an array with %i bit integer values is not unsupported." %
self.bytes_per_element)
@property
def bytes_array(self):
'''Get the parameter data as an array of raw byte strings.'''
# Decode different dimensions
if len(self.dimensions) == 0:
return np.array([])
elif len(self.dimensions) == 1:
return np.array(self._data.bytes)
else:
# Convert Fortran shape (data in memory is identical, shape is transposed)
word_len = self.dimensions[0]
dims = self.dimensions[1:][::-1] # Identical to: [:0:-1]
byte_steps = np.cumprod(self.dimensions[:-1])[::-1]
# Generate mult-dimensional array and parse byte words
byte_arr = np.empty(dims, dtype=object)
for i in np.ndindex(*dims):
# Calculate byte offset as sum of each array index times the byte step of each dimension.
off = np.sum(np.multiply(i, byte_steps))
byte_arr[i] = self._data.bytes[off:off+word_len]
return byte_arr
@property
def string_array(self):
'''Get the parameter data as a python array of unicode strings.'''
# Decode different dimensions
if len(self.dimensions) == 0:
return np.array([])
elif len(self.dimensions) == 1:
return np.array([self.string_value])
else:
# Parse byte sequences
byte_arr = self.bytes_array
# Decode sequences
for i in np.ndindex(byte_arr.shape):
byte_arr[i] = self.dtypes.decode_string(byte_arr[i])
return byte_arr
@property
def any_value(self):
''' Get the parameter data as a value of 'traditional type'.
Traditional types are defined in the Parameter section in the [user manual].
Returns
-------
value : int, float, or str
Depending on the `bytes_per_element` field, a traditional type can
be a either a signed byte, signed short, 32-bit float, or a string.
[user manual]: https://www.c3d.org/docs/C3D_User_Guide.pdf
'''
if self.bytes_per_element >= 4:
return self.float_value
elif self.bytes_per_element >= 2:
return self.int16_value
elif self.bytes_per_element == -1:
return self.string_value
else:
return self.int8_value
@property
def any_array(self):
''' Get the parameter data as an array of 'traditional type'.
Traditional types are defined in the Parameter section in the [user manual].
Returns
-------
value : array
Depending on the `bytes_per_element` field, a traditional type can
be a either a signed byte, signed short, 32-bit float, or a string.
[user manual]: https://www.c3d.org/docs/C3D_User_Guide.pdf
'''
if self.bytes_per_element >= 4:
return self.float_array
elif self.bytes_per_element >= 2:
return self.int16_array
elif self.bytes_per_element == -1:
return self.string_array
else:
return self.int8_array
@property
def _as_any_uint(self):
''' Attempt to parse the parameter data as any unsigned integer format.
Checks if the integer is stored as a floating point value.
Can be used to read 'POINT:FRAMES' or 'POINT:LONG_FRAMES'
when not accessed through `c3d.manager.Manager.last_frame`.
'''
if self.bytes_per_element >= 4:
# Check if float value representation is an integer
value = self.float_value
if float(value).is_integer():
return int(value)
return self.uint32_value
elif self.bytes_per_element >= 2:
return self.uint16_value
else:
return self.uint8_value
class Param(ParamReadonly):
''' Wrapper exposing both readable and writable attributes of a `c3d.parameter.ParamData` entry.
'''
def __init__(self, data):
super(Param, self).__init__(data)
def readonly(self):
''' Returns a readonly `c3d.parameter.ParamReadonly` instance. '''
return ParamReadonly(self._data)
@property
def bytes(self) -> bytes:
''' Get or set the parameter bytes. '''
return self._data.bytes
@bytes.setter
def bytes(self, value):
self._data.bytes = value
Classes
class Param (data)
-
Wrapper exposing both readable and writable attributes of a
ParamData
entry.Expand source code
class Param(ParamReadonly): ''' Wrapper exposing both readable and writable attributes of a `c3d.parameter.ParamData` entry. ''' def __init__(self, data): super(Param, self).__init__(data) def readonly(self): ''' Returns a readonly `c3d.parameter.ParamReadonly` instance. ''' return ParamReadonly(self._data) @property def bytes(self) -> bytes: ''' Get or set the parameter bytes. ''' return self._data.bytes @bytes.setter def bytes(self, value): self._data.bytes = value
Ancestors
Instance variables
var bytes : bytes
-
Get or set the parameter bytes.
Expand source code
@property def bytes(self) -> bytes: ''' Get or set the parameter bytes. ''' return self._data.bytes
Methods
def readonly(self)
-
Returns a readonly
ParamReadonly
instance.Expand source code
def readonly(self): ''' Returns a readonly `c3d.parameter.ParamReadonly` instance. ''' return ParamReadonly(self._data)
Inherited members
ParamReadonly
:any_array
any_value
binary_size
bytes_array
bytes_per_element
bytes_value
desc
dimensions
dtypes
float32_array
float64_array
float_array
float_value
int16_array
int16_value
int32_array
int32_value
int64_array
int8_array
int8_value
int_array
int_value
name
num_elements
string_array
string_value
total_bytes
uint16_array
uint16_value
uint32_array
uint32_value
uint64_array
uint8_array
uint8_value
uint_array
uint_value
class ParamData (name, dtype, desc='', bytes_per_element=1, dimensions=None, bytes=b'', handle=None)
-
A class representing a single named parameter from a C3D file.
Attributes
name
:str
- Name of this parameter.
dtype
:DataTypes
- Reference to the DataTypes object associated with the file.
desc
:str
- Brief description of this parameter.
bytes_per_element
:int
, optional- For array data, this describes the size of each element of data. For string data (including arrays of strings), this should be -1.
dimensions
:list
ofint
- For array data, this describes the dimensions of the array, stored in column-major (Fortran) order. For arrays of strings, the dimensions here will be the number of columns (length of each string) followed by the number of rows (number of strings).
bytes
:str
- Raw data for this parameter.
Set up a new parameter, only the name is required.
Expand source code
class ParamData(object): '''A class representing a single named parameter from a C3D file. Attributes ---------- name : str Name of this parameter. dtype: DataTypes Reference to the DataTypes object associated with the file. desc : str Brief description of this parameter. bytes_per_element : int, optional For array data, this describes the size of each element of data. For string data (including arrays of strings), this should be -1. dimensions : list of int For array data, this describes the dimensions of the array, stored in column-major (Fortran) order. For arrays of strings, the dimensions here will be the number of columns (length of each string) followed by the number of rows (number of strings). bytes : str Raw data for this parameter. ''' def __init__(self, name, dtype, desc='', bytes_per_element=1, dimensions=None, bytes=b'', handle=None): '''Set up a new parameter, only the name is required.''' self.name = name self.dtypes = dtype self.desc = desc self.bytes_per_element = bytes_per_element self.dimensions = dimensions or [] self.bytes = bytes if handle: self.read(handle) def __repr__(self): return '<Param: {}>'.format(self.desc) @property def num_elements(self) -> int: '''Return the number of elements in this parameter's array value.''' e = 1 for d in self.dimensions: e *= d return e @property def total_bytes(self) -> int: '''Return the number of bytes used for storing this parameter's data.''' return self.num_elements * abs(self.bytes_per_element) @property def binary_size(self) -> int: '''Return the number of bytes needed to store this parameter.''' return ( 1 + # group_id 2 + # next offset marker 1 + len(self.name.encode('utf-8')) + # size of name and name bytes 1 + # data size # size of dimensions and dimension bytes 1 + len(self.dimensions) + self.total_bytes + # data 1 + len(self.desc.encode('utf-8')) # size of desc and desc bytes ) def write(self, group_id, handle): '''Write binary data for this parameter to a file handle. Parameters ---------- group_id : int The numerical ID of the group that holds this parameter. handle : file handle An open, writable, binary file handle. ''' name = self.name.encode('utf-8') handle.write(struct.pack('bb', len(name), group_id)) handle.write(name) handle.write(struct.pack('<h', self.binary_size - 2 - len(name))) handle.write(struct.pack('b', self.bytes_per_element)) handle.write(struct.pack('B', len(self.dimensions))) handle.write(struct.pack('B' * len(self.dimensions), *self.dimensions)) if self.bytes is not None and len(self.bytes) > 0: handle.write(self.bytes) desc = self.desc.encode('utf-8') handle.write(struct.pack('B', len(desc))) handle.write(desc) def read(self, handle): '''Read binary data for this parameter from a file handle. This reads exactly enough data from the current position in the file to initialize the parameter. ''' self.bytes_per_element, = struct.unpack('b', handle.read(1)) dims, = struct.unpack('B', handle.read(1)) self.dimensions = [struct.unpack('B', handle.read(1))[ 0] for _ in range(dims)] self.bytes = b'' if self.total_bytes: self.bytes = handle.read(self.total_bytes) desc_size, = struct.unpack('B', handle.read(1)) self.desc = desc_size and self.dtypes.decode_string(handle.read(desc_size)) or '' def _as(self, dtype): '''Unpack the raw bytes of this param using the given struct format.''' return np.frombuffer(self.bytes, count=1, dtype=dtype)[0] def _as_array(self, dtype, copy=True): '''Unpack the raw bytes of this param using the given data format.''' if not self.dimensions: return [self._as(dtype)] elems = np.frombuffer(self.bytes, dtype=dtype) # Reverse shape as the shape is defined in fortran format view = elems.reshape(self.dimensions[::-1]) if copy: return view.copy() return view
Instance variables
var binary_size : int
-
Return the number of bytes needed to store this parameter.
Expand source code
@property def binary_size(self) -> int: '''Return the number of bytes needed to store this parameter.''' return ( 1 + # group_id 2 + # next offset marker 1 + len(self.name.encode('utf-8')) + # size of name and name bytes 1 + # data size # size of dimensions and dimension bytes 1 + len(self.dimensions) + self.total_bytes + # data 1 + len(self.desc.encode('utf-8')) # size of desc and desc bytes )
var num_elements : int
-
Return the number of elements in this parameter's array value.
Expand source code
@property def num_elements(self) -> int: '''Return the number of elements in this parameter's array value.''' e = 1 for d in self.dimensions: e *= d return e
var total_bytes : int
-
Return the number of bytes used for storing this parameter's data.
Expand source code
@property def total_bytes(self) -> int: '''Return the number of bytes used for storing this parameter's data.''' return self.num_elements * abs(self.bytes_per_element)
Methods
def read(self, handle)
-
Read binary data for this parameter from a file handle.
This reads exactly enough data from the current position in the file to initialize the parameter.
Expand source code
def read(self, handle): '''Read binary data for this parameter from a file handle. This reads exactly enough data from the current position in the file to initialize the parameter. ''' self.bytes_per_element, = struct.unpack('b', handle.read(1)) dims, = struct.unpack('B', handle.read(1)) self.dimensions = [struct.unpack('B', handle.read(1))[ 0] for _ in range(dims)] self.bytes = b'' if self.total_bytes: self.bytes = handle.read(self.total_bytes) desc_size, = struct.unpack('B', handle.read(1)) self.desc = desc_size and self.dtypes.decode_string(handle.read(desc_size)) or ''
def write(self, group_id, handle)
-
Write binary data for this parameter to a file handle.
Parameters
group_id
:int
- The numerical ID of the group that holds this parameter.
handle
:file handle
- An open, writable, binary file handle.
Expand source code
def write(self, group_id, handle): '''Write binary data for this parameter to a file handle. Parameters ---------- group_id : int The numerical ID of the group that holds this parameter. handle : file handle An open, writable, binary file handle. ''' name = self.name.encode('utf-8') handle.write(struct.pack('bb', len(name), group_id)) handle.write(name) handle.write(struct.pack('<h', self.binary_size - 2 - len(name))) handle.write(struct.pack('b', self.bytes_per_element)) handle.write(struct.pack('B', len(self.dimensions))) handle.write(struct.pack('B' * len(self.dimensions), *self.dimensions)) if self.bytes is not None and len(self.bytes) > 0: handle.write(self.bytes) desc = self.desc.encode('utf-8') handle.write(struct.pack('B', len(desc))) handle.write(desc)
class ParamReadonly (data)
-
Wrapper exposing readonly attributes of a
ParamData
entry.Expand source code
class ParamReadonly(object): ''' Wrapper exposing readonly attributes of a `c3d.parameter.ParamData` entry. ''' def __init__(self, data): self._data = data def __eq__(self, other): return self._data is other._data @property def name(self) -> str: ''' Get the parameter name. ''' return self._data.name @property def desc(self) -> str: ''' Get the parameter descriptor. ''' return self._data.desc @property def dtypes(self): ''' Convenience accessor to the `c3d.dtypes.DataTypes` instance associated with the parameter. ''' return self._data.dtypes @property def dimensions(self) -> (int, ...): ''' Shape of the parameter data (Fortran format). ''' return self._data.dimensions @property def num_elements(self) -> int: '''Return the number of elements in this parameter's array value.''' return self._data.num_elements @property def bytes_per_element(self) -> int: '''Return the number of bytes used to store each data element.''' return self._data.bytes_per_element @property def total_bytes(self) -> int: '''Return the number of bytes used for storing this parameter's data.''' return self._data.total_bytes @property def binary_size(self) -> int: '''Return the number of bytes needed to store this parameter.''' return self._data.binary_size @property def int8_value(self): '''Get the parameter data as an 8-bit signed integer.''' return self._data._as(self.dtypes.int8) @property def uint8_value(self): '''Get the parameter data as an 8-bit unsigned integer.''' return self._data._as(self.dtypes.uint8) @property def int16_value(self): '''Get the parameter data as a 16-bit signed integer.''' return self._data._as(self.dtypes.int16) @property def uint16_value(self): '''Get the parameter data as a 16-bit unsigned integer.''' return self._data._as(self.dtypes.uint16) @property def int32_value(self): '''Get the parameter data as a 32-bit signed integer.''' return self._data._as(self.dtypes.int32) @property def uint32_value(self): '''Get the parameter data as a 32-bit unsigned integer.''' return self._data._as(self.dtypes.uint32) @property def uint_value(self): ''' Get the parameter data as a unsigned integer of appropriate type. ''' if self.bytes_per_element >= 4: return self.uint32_value elif self.bytes_per_element >= 2: return self.uint16_value else: return self.uint8_value @property def int_value(self): ''' Get the parameter data as a signed integer of appropriate type. ''' if self.bytes_per_element >= 4: return self.int32_value elif self.bytes_per_element >= 2: return self.int16_value else: return self.int8_value @property def float_value(self): '''Get the parameter data as a floating point value of appropriate type.''' if self.bytes_per_element > 4: if self.dtypes.is_dec: raise AttributeError("64 bit DEC floating point is not supported.") # 64-bit floating point is not a standard return self._data._as(self.dtypes.float64) elif self.bytes_per_element == 4: if self.dtypes.is_dec: return DEC_to_IEEE(self._data._as(np.uint32)) else: # is_mips or is_ieee return self._data._as(self.dtypes.float32) else: raise AttributeError("Only 32 and 64 bit floating point is supported.") @property def bytes_value(self) -> bytes: '''Get the raw byte string.''' return self._data.bytes @property def string_value(self): '''Get the parameter data as a unicode string.''' return self.dtypes.decode_string(self._data.bytes) @property def int8_array(self): '''Get the parameter data as an array of 8-bit signed integers.''' return self._data._as_array(self.dtypes.int8) @property def uint8_array(self): '''Get the parameter data as an array of 8-bit unsigned integers.''' return self._data._as_array(self.dtypes.uint8) @property def int16_array(self): '''Get the parameter data as an array of 16-bit signed integers.''' return self._data._as_array(self.dtypes.int16) @property def uint16_array(self): '''Get the parameter data as an array of 16-bit unsigned integers.''' return self._data._as_array(self.dtypes.uint16) @property def int32_array(self): '''Get the parameter data as an array of 32-bit signed integers.''' return self._data._as_array(self.dtypes.int32) @property def uint32_array(self): '''Get the parameter data as an array of 32-bit unsigned integers.''' return self._data._as_array(self.dtypes.uint32) @property def int64_array(self): '''Get the parameter data as an array of 32-bit signed integers.''' return self._data._as_array(self.dtypes.int64) @property def uint64_array(self): '''Get the parameter data as an array of 32-bit unsigned integers.''' return self._data._as_array(self.dtypes.uint64) @property def float32_array(self): '''Get the parameter data as an array of 32-bit floats.''' # Convert float data if not IEEE processor if self.dtypes.is_dec: # _as_array but for DEC if not self.dimensions: return [self.float_value] return DEC_to_IEEE_BYTES(self._data.bytes).reshape(self.dimensions[::-1]) # Reverse fortran format else: # is_ieee or is_mips return self._data._as_array(self.dtypes.float32) @property def float64_array(self): '''Get the parameter data as an array of 64-bit floats.''' # Convert float data if not IEEE processor if self.dtypes.is_dec: raise ValueError('Unable to convert bytes encoded in a 64 bit floating point DEC format.') else: # is_ieee or is_mips return self._data._as_array(self.dtypes.float64) @property def float_array(self): '''Get the parameter data as an array of 32 or 64 bit floats.''' # Convert float data if not IEEE processor if self.bytes_per_element == 4: return self.float32_array elif self.bytes_per_element == 8: return self.float64_array else: raise TypeError("Parsing parameter bytes to an array with %i bit " % self.bytes_per_element + "floating-point precission is not unsupported.") @property def int_array(self): '''Get the parameter data as an array of integer values.''' # Convert float data if not IEEE processor if self.bytes_per_element == 1: return self.int8_array elif self.bytes_per_element == 2: return self.int16_array elif self.bytes_per_element == 4: return self.int32_array elif self.bytes_per_element == 8: return self.int64_array else: raise TypeError("Parsing parameter bytes to an array with %i bit integer values is not unsupported." % self.bytes_per_element) @property def uint_array(self): '''Get the parameter data as an array of integer values.''' # Convert float data if not IEEE processor if self.bytes_per_element == 1: return self.uint8_array elif self.bytes_per_element == 2: return self.uint16_array elif self.bytes_per_element == 4: return self.uint32_array elif self.bytes_per_element == 8: return self.uint64_array else: raise TypeError("Parsing parameter bytes to an array with %i bit integer values is not unsupported." % self.bytes_per_element) @property def bytes_array(self): '''Get the parameter data as an array of raw byte strings.''' # Decode different dimensions if len(self.dimensions) == 0: return np.array([]) elif len(self.dimensions) == 1: return np.array(self._data.bytes) else: # Convert Fortran shape (data in memory is identical, shape is transposed) word_len = self.dimensions[0] dims = self.dimensions[1:][::-1] # Identical to: [:0:-1] byte_steps = np.cumprod(self.dimensions[:-1])[::-1] # Generate mult-dimensional array and parse byte words byte_arr = np.empty(dims, dtype=object) for i in np.ndindex(*dims): # Calculate byte offset as sum of each array index times the byte step of each dimension. off = np.sum(np.multiply(i, byte_steps)) byte_arr[i] = self._data.bytes[off:off+word_len] return byte_arr @property def string_array(self): '''Get the parameter data as a python array of unicode strings.''' # Decode different dimensions if len(self.dimensions) == 0: return np.array([]) elif len(self.dimensions) == 1: return np.array([self.string_value]) else: # Parse byte sequences byte_arr = self.bytes_array # Decode sequences for i in np.ndindex(byte_arr.shape): byte_arr[i] = self.dtypes.decode_string(byte_arr[i]) return byte_arr @property def any_value(self): ''' Get the parameter data as a value of 'traditional type'. Traditional types are defined in the Parameter section in the [user manual]. Returns ------- value : int, float, or str Depending on the `bytes_per_element` field, a traditional type can be a either a signed byte, signed short, 32-bit float, or a string. [user manual]: https://www.c3d.org/docs/C3D_User_Guide.pdf ''' if self.bytes_per_element >= 4: return self.float_value elif self.bytes_per_element >= 2: return self.int16_value elif self.bytes_per_element == -1: return self.string_value else: return self.int8_value @property def any_array(self): ''' Get the parameter data as an array of 'traditional type'. Traditional types are defined in the Parameter section in the [user manual]. Returns ------- value : array Depending on the `bytes_per_element` field, a traditional type can be a either a signed byte, signed short, 32-bit float, or a string. [user manual]: https://www.c3d.org/docs/C3D_User_Guide.pdf ''' if self.bytes_per_element >= 4: return self.float_array elif self.bytes_per_element >= 2: return self.int16_array elif self.bytes_per_element == -1: return self.string_array else: return self.int8_array @property def _as_any_uint(self): ''' Attempt to parse the parameter data as any unsigned integer format. Checks if the integer is stored as a floating point value. Can be used to read 'POINT:FRAMES' or 'POINT:LONG_FRAMES' when not accessed through `c3d.manager.Manager.last_frame`. ''' if self.bytes_per_element >= 4: # Check if float value representation is an integer value = self.float_value if float(value).is_integer(): return int(value) return self.uint32_value elif self.bytes_per_element >= 2: return self.uint16_value else: return self.uint8_value
Subclasses
Instance variables
var any_array
-
Get the parameter data as an array of 'traditional type'.
Traditional types are defined in the Parameter section in the user manual.
Returns
value
:array
- Depending on the
bytes_per_element
field, a traditional type can be a either a signed byte, signed short, 32-bit float, or a string.
Expand source code
@property def any_array(self): ''' Get the parameter data as an array of 'traditional type'. Traditional types are defined in the Parameter section in the [user manual]. Returns ------- value : array Depending on the `bytes_per_element` field, a traditional type can be a either a signed byte, signed short, 32-bit float, or a string. [user manual]: https://www.c3d.org/docs/C3D_User_Guide.pdf ''' if self.bytes_per_element >= 4: return self.float_array elif self.bytes_per_element >= 2: return self.int16_array elif self.bytes_per_element == -1: return self.string_array else: return self.int8_array
var any_value
-
Get the parameter data as a value of 'traditional type'.
Traditional types are defined in the Parameter section in the user manual.
Returns
value
:int, float,
orstr
- Depending on the
bytes_per_element
field, a traditional type can be a either a signed byte, signed short, 32-bit float, or a string.
Expand source code
@property def any_value(self): ''' Get the parameter data as a value of 'traditional type'. Traditional types are defined in the Parameter section in the [user manual]. Returns ------- value : int, float, or str Depending on the `bytes_per_element` field, a traditional type can be a either a signed byte, signed short, 32-bit float, or a string. [user manual]: https://www.c3d.org/docs/C3D_User_Guide.pdf ''' if self.bytes_per_element >= 4: return self.float_value elif self.bytes_per_element >= 2: return self.int16_value elif self.bytes_per_element == -1: return self.string_value else: return self.int8_value
var binary_size : int
-
Return the number of bytes needed to store this parameter.
Expand source code
@property def binary_size(self) -> int: '''Return the number of bytes needed to store this parameter.''' return self._data.binary_size
var bytes_array
-
Get the parameter data as an array of raw byte strings.
Expand source code
@property def bytes_array(self): '''Get the parameter data as an array of raw byte strings.''' # Decode different dimensions if len(self.dimensions) == 0: return np.array([]) elif len(self.dimensions) == 1: return np.array(self._data.bytes) else: # Convert Fortran shape (data in memory is identical, shape is transposed) word_len = self.dimensions[0] dims = self.dimensions[1:][::-1] # Identical to: [:0:-1] byte_steps = np.cumprod(self.dimensions[:-1])[::-1] # Generate mult-dimensional array and parse byte words byte_arr = np.empty(dims, dtype=object) for i in np.ndindex(*dims): # Calculate byte offset as sum of each array index times the byte step of each dimension. off = np.sum(np.multiply(i, byte_steps)) byte_arr[i] = self._data.bytes[off:off+word_len] return byte_arr
var bytes_per_element : int
-
Return the number of bytes used to store each data element.
Expand source code
@property def bytes_per_element(self) -> int: '''Return the number of bytes used to store each data element.''' return self._data.bytes_per_element
var bytes_value : bytes
-
Get the raw byte string.
Expand source code
@property def bytes_value(self) -> bytes: '''Get the raw byte string.''' return self._data.bytes
var desc : str
-
Get the parameter descriptor.
Expand source code
@property def desc(self) -> str: ''' Get the parameter descriptor. ''' return self._data.desc
var dimensions : (
, Ellipsis) -
Shape of the parameter data (Fortran format).
Expand source code
@property def dimensions(self) -> (int, ...): ''' Shape of the parameter data (Fortran format). ''' return self._data.dimensions
var dtypes
-
Convenience accessor to the
DataTypes
instance associated with the parameter.Expand source code
@property def dtypes(self): ''' Convenience accessor to the `c3d.dtypes.DataTypes` instance associated with the parameter. ''' return self._data.dtypes
var float32_array
-
Get the parameter data as an array of 32-bit floats.
Expand source code
@property def float32_array(self): '''Get the parameter data as an array of 32-bit floats.''' # Convert float data if not IEEE processor if self.dtypes.is_dec: # _as_array but for DEC if not self.dimensions: return [self.float_value] return DEC_to_IEEE_BYTES(self._data.bytes).reshape(self.dimensions[::-1]) # Reverse fortran format else: # is_ieee or is_mips return self._data._as_array(self.dtypes.float32)
var float64_array
-
Get the parameter data as an array of 64-bit floats.
Expand source code
@property def float64_array(self): '''Get the parameter data as an array of 64-bit floats.''' # Convert float data if not IEEE processor if self.dtypes.is_dec: raise ValueError('Unable to convert bytes encoded in a 64 bit floating point DEC format.') else: # is_ieee or is_mips return self._data._as_array(self.dtypes.float64)
var float_array
-
Get the parameter data as an array of 32 or 64 bit floats.
Expand source code
@property def float_array(self): '''Get the parameter data as an array of 32 or 64 bit floats.''' # Convert float data if not IEEE processor if self.bytes_per_element == 4: return self.float32_array elif self.bytes_per_element == 8: return self.float64_array else: raise TypeError("Parsing parameter bytes to an array with %i bit " % self.bytes_per_element + "floating-point precission is not unsupported.")
var float_value
-
Get the parameter data as a floating point value of appropriate type.
Expand source code
@property def float_value(self): '''Get the parameter data as a floating point value of appropriate type.''' if self.bytes_per_element > 4: if self.dtypes.is_dec: raise AttributeError("64 bit DEC floating point is not supported.") # 64-bit floating point is not a standard return self._data._as(self.dtypes.float64) elif self.bytes_per_element == 4: if self.dtypes.is_dec: return DEC_to_IEEE(self._data._as(np.uint32)) else: # is_mips or is_ieee return self._data._as(self.dtypes.float32) else: raise AttributeError("Only 32 and 64 bit floating point is supported.")
var int16_array
-
Get the parameter data as an array of 16-bit signed integers.
Expand source code
@property def int16_array(self): '''Get the parameter data as an array of 16-bit signed integers.''' return self._data._as_array(self.dtypes.int16)
var int16_value
-
Get the parameter data as a 16-bit signed integer.
Expand source code
@property def int16_value(self): '''Get the parameter data as a 16-bit signed integer.''' return self._data._as(self.dtypes.int16)
var int32_array
-
Get the parameter data as an array of 32-bit signed integers.
Expand source code
@property def int32_array(self): '''Get the parameter data as an array of 32-bit signed integers.''' return self._data._as_array(self.dtypes.int32)
var int32_value
-
Get the parameter data as a 32-bit signed integer.
Expand source code
@property def int32_value(self): '''Get the parameter data as a 32-bit signed integer.''' return self._data._as(self.dtypes.int32)
var int64_array
-
Get the parameter data as an array of 32-bit signed integers.
Expand source code
@property def int64_array(self): '''Get the parameter data as an array of 32-bit signed integers.''' return self._data._as_array(self.dtypes.int64)
var int8_array
-
Get the parameter data as an array of 8-bit signed integers.
Expand source code
@property def int8_array(self): '''Get the parameter data as an array of 8-bit signed integers.''' return self._data._as_array(self.dtypes.int8)
var int8_value
-
Get the parameter data as an 8-bit signed integer.
Expand source code
@property def int8_value(self): '''Get the parameter data as an 8-bit signed integer.''' return self._data._as(self.dtypes.int8)
var int_array
-
Get the parameter data as an array of integer values.
Expand source code
@property def int_array(self): '''Get the parameter data as an array of integer values.''' # Convert float data if not IEEE processor if self.bytes_per_element == 1: return self.int8_array elif self.bytes_per_element == 2: return self.int16_array elif self.bytes_per_element == 4: return self.int32_array elif self.bytes_per_element == 8: return self.int64_array else: raise TypeError("Parsing parameter bytes to an array with %i bit integer values is not unsupported." % self.bytes_per_element)
var int_value
-
Get the parameter data as a signed integer of appropriate type.
Expand source code
@property def int_value(self): ''' Get the parameter data as a signed integer of appropriate type. ''' if self.bytes_per_element >= 4: return self.int32_value elif self.bytes_per_element >= 2: return self.int16_value else: return self.int8_value
var name : str
-
Get the parameter name.
Expand source code
@property def name(self) -> str: ''' Get the parameter name. ''' return self._data.name
var num_elements : int
-
Return the number of elements in this parameter's array value.
Expand source code
@property def num_elements(self) -> int: '''Return the number of elements in this parameter's array value.''' return self._data.num_elements
var string_array
-
Get the parameter data as a python array of unicode strings.
Expand source code
@property def string_array(self): '''Get the parameter data as a python array of unicode strings.''' # Decode different dimensions if len(self.dimensions) == 0: return np.array([]) elif len(self.dimensions) == 1: return np.array([self.string_value]) else: # Parse byte sequences byte_arr = self.bytes_array # Decode sequences for i in np.ndindex(byte_arr.shape): byte_arr[i] = self.dtypes.decode_string(byte_arr[i]) return byte_arr
var string_value
-
Get the parameter data as a unicode string.
Expand source code
@property def string_value(self): '''Get the parameter data as a unicode string.''' return self.dtypes.decode_string(self._data.bytes)
var total_bytes : int
-
Return the number of bytes used for storing this parameter's data.
Expand source code
@property def total_bytes(self) -> int: '''Return the number of bytes used for storing this parameter's data.''' return self._data.total_bytes
var uint16_array
-
Get the parameter data as an array of 16-bit unsigned integers.
Expand source code
@property def uint16_array(self): '''Get the parameter data as an array of 16-bit unsigned integers.''' return self._data._as_array(self.dtypes.uint16)
var uint16_value
-
Get the parameter data as a 16-bit unsigned integer.
Expand source code
@property def uint16_value(self): '''Get the parameter data as a 16-bit unsigned integer.''' return self._data._as(self.dtypes.uint16)
var uint32_array
-
Get the parameter data as an array of 32-bit unsigned integers.
Expand source code
@property def uint32_array(self): '''Get the parameter data as an array of 32-bit unsigned integers.''' return self._data._as_array(self.dtypes.uint32)
var uint32_value
-
Get the parameter data as a 32-bit unsigned integer.
Expand source code
@property def uint32_value(self): '''Get the parameter data as a 32-bit unsigned integer.''' return self._data._as(self.dtypes.uint32)
var uint64_array
-
Get the parameter data as an array of 32-bit unsigned integers.
Expand source code
@property def uint64_array(self): '''Get the parameter data as an array of 32-bit unsigned integers.''' return self._data._as_array(self.dtypes.uint64)
var uint8_array
-
Get the parameter data as an array of 8-bit unsigned integers.
Expand source code
@property def uint8_array(self): '''Get the parameter data as an array of 8-bit unsigned integers.''' return self._data._as_array(self.dtypes.uint8)
var uint8_value
-
Get the parameter data as an 8-bit unsigned integer.
Expand source code
@property def uint8_value(self): '''Get the parameter data as an 8-bit unsigned integer.''' return self._data._as(self.dtypes.uint8)
var uint_array
-
Get the parameter data as an array of integer values.
Expand source code
@property def uint_array(self): '''Get the parameter data as an array of integer values.''' # Convert float data if not IEEE processor if self.bytes_per_element == 1: return self.uint8_array elif self.bytes_per_element == 2: return self.uint16_array elif self.bytes_per_element == 4: return self.uint32_array elif self.bytes_per_element == 8: return self.uint64_array else: raise TypeError("Parsing parameter bytes to an array with %i bit integer values is not unsupported." % self.bytes_per_element)
var uint_value
-
Get the parameter data as a unsigned integer of appropriate type.
Expand source code
@property def uint_value(self): ''' Get the parameter data as a unsigned integer of appropriate type. ''' if self.bytes_per_element >= 4: return self.uint32_value elif self.bytes_per_element >= 2: return self.uint16_value else: return self.uint8_value