Isotropix Forums

Exporting property values as double

Clarisse Scripting related topics

Exporting property values as double

Unread postby jboissinot » Tue Jan 29, 2019 10:48 pm

Hi there,

I've been dealing with some issues for exporting the values of items on a property as double.

Everything works well if we set the ResourceProperty object with TYPE_FLOAT_64 and its size to 3, and set the values with the set_double() method.
But the properties are not properly exported when using a size that's bigger than 3 like 4 or 16 for matrix transform values for instance.
We also tried with setting the type to TYPE_FLOAT_32 but it doesn't work either and actually returns the values as float instead of double.

As such, I was wondering if we were dealing with some kind of limitation here and if it'd be possible to do this.

Just putting this little code snippet to describe the question.
Code: Select all
import random

nbItems = 10

prop = createProperty('test')
prop.init(ix.api.ResourceProperty.TYPE_FLOAT_64, 4, nbItems)

for i in range(nbItems):
   for v in range(4):
      prop.set_double(i, random.uniform(), v)

Any help or thoughts on this would be appreciated.

Posts: 4
Joined: Tue Jan 29, 2019 10:36 pm

Re: Exporting property values as double

Unread postby bvaldes » Wed Jan 30, 2019 12:13 pm


You should take a look to this post. I create a matrix property on this script: viewtopic.php?f=21&t=5100&p=17893#p17893

Tell me if this is not what you are looking for.

Clarisse QA
User avatar
Posts: 315
Joined: Mon Sep 26, 2016 10:44 am

Re: Exporting property values as double

Unread postby anemoff » Wed Jan 30, 2019 12:32 pm

Just wanted to add the list of "standard" Alembic geometry property types that should be supported by applications, including Clarisse:

size 1:
- int8, uint8
- int16, uint16
- int32, uint32
- int64, uint64
- float16
- float32
- float64

size 2
- int16 interpreted as vec2
- int 32 interpreted as vec2
- float32 interpreted as vec2
- float64 interpreted as vec2

size 3
- uint8 interpreted as color3
- int16 interpreted as vec3
- int32 interpreted as vec3
- float16 interpreted as color3
- float32 interpreted as vec3
- float64 interpreted as vec3

size 4
- uint8 interpreted as color4
- int16 interpreted as box2 (2 x 2D points)
- int32 interpreted as box2
- float16 interpreted as color4
- float32 interpreted as quaternion
- float64 interpreted as quaternion

size 6
- int16 interpreted as box3 (2 x 3D points)
- int32 interpreted as box3
- float32 interpreted as box3
- float64 interpreted as box3

size 9
- float32 interpreted as matrix3x3
- float64 interpreted as matrix3x3

size 16
- float32 interpreted as matrix4x4
- float64 interpreted as matrix4x4

Other properties with non-standard type-size combinations are supported on import by Clarisse but might not be supported by other applications (Maya, Houdini, ...) because they are not standard Alembic geometry properties. They are just regular properties. For example a property of type float32 with size 5 is not an Alembic geometry property.

Also, can you send us the details of your "createProperty" function? Maybe there's an issue in there.

Anthony Nemoff
R&D Engineer
User avatar
Posts: 87
Joined: Wed Jan 13, 2016 10:10 am

Re: Exporting property values as double

Unread postby jboissinot » Wed Jan 30, 2019 11:38 pm



We did use the matrix property method you're referring too. The only thing that I was doing differently though was to store all items of the matrix in a list and parse that list for setting the values in the property. Even though the values seemed to be correct when printing both while testing it, it appears that some values were wrong or undefined on the end when being loaded in other applications. Setting the values in a property directly while parsing the matrix seems to have fixed the issue as all the values come correctly now. I guess it might be a restriction to not store the matrix items in a list and have to be set with the matrix object but not quite sure about this and you'd may confirm this. But following what Anthony sent, I think it was just a matter of setting the values as a matrix4x4 and not a list of size 16.

Despite that, we still have the issue for exporting the values as double as they come completely wrong in other applications when using the set_double() function. We found out in the scripting documentation that the vec3 and matrix4x4 objects can support double and float values, and we were wondering if we'd need to define this in the object to be able to set the proper data type.


Thanks for sharing this list of property types, this is definitely relevant and helpful information.
We were actually setting the values of the property with a custom list of size 4 which was wrong.
So we tried the quaternion method by storing the rotation angle value and the rotation axis as a vec3 in a quat object as you can see in the code below.

Code: Select all
rotate = ix.api.GMathVec3d()

xRot = rotate[0]
yRot = rotate[1]
zRot = rotate[2]

xRotAxis = ix.api.GMathVec3d(1,0,0)
yRotAxis = ix.api.GMathVec3d(0,1,0)
zRotAxis = ix.api.GMathVec3d(0,0,1)

xRotQuat = ix.api.GMathQuat()
xRotQuat.set_rotation(xRot, xRotAxis)

yRotQuat = ix.api.GMathQuat()
yRotQuat.set_rotation(yRot, yRotAxis)

zRotQuat = ix.api.GMathQuat()
zRotQuat.set_rotation(zRot, zRotAxis)

for v in range(4):
        instanceRotateX.set_double(i, xRotQuat[v], v)
for v in range(4):
        instanceRotateY.set_double(i, yRotQuat[v], v)
for v in range(4):
        instanceRotateZ.set_double(i, zRotQuat[v], v)

We're getting the following error when running the script:
AttributeError: 'module' object has no attribute 'GMathQuat'

We're not actually quite sure on how to create a quaternion object, this is just how we did it based on the documentation.

Thanks for your help,
Posts: 4
Joined: Tue Jan 29, 2019 10:36 pm

Re: Exporting property values as double

Unread postby anemoff » Fri Feb 01, 2019 6:42 pm

Actually, GMathQuat isn't available in python, hence your error.
This is a bug and we have added it to our bug database with the ID #9247.
We will add GMathQuat to the python API.

You can use a GMathVec4 just for storing 4 double values. But you won't have the quaternion API, it'll just serve as an array of 4 doubles.
Anthony Nemoff
R&D Engineer
User avatar
Posts: 87
Joined: Wed Jan 13, 2016 10:10 am

Re: Exporting property values as double

Unread postby anemoff » Mon Feb 11, 2019 4:18 pm


Here's a script that uses the IOHelpers API to create and edit the point properties instead of using directly the ResourceProperty API.
The IOHelpers API is mainly dedicated to edit ParticleContainer objects, so I hope this is your case.
Please note that currently the Property Editor isn't notified when a new property is added via IOHelpers. Close it and open a new one to show all properties.

For the sake of testing, I'm creating matrices with random values, but you just have to replace that with your real matrices.
I have attached the script and a project that uses it.

I hope this answers your needs. Let me know if you have questions.
We will also investigate the set_double issue.

python code

import random

INVALID_INDEX = -1 + 2**32


def log_debug(msg):
ix.log_info('[DEBUG] %r' % msg)

# -------------------------------------------------------------------
# Helpers for ResourceProperty.

value_types = {
0 : 'int8',
1 : 'uint8',
2 : 'int16',
3 : 'uint16',
4 : 'int32',
5 : 'uint32',
6 : 'int64',
7 : 'uint64',
8 : 'float16',
9 : 'float32',
10 : 'float64',
11 : 'char',
12 : 'wchar',

def get_type_size(value_type):
Get the property type size in bytes. Example: float32 -> 4 bytes.
return ix.api.ResourceProperty.get_type_size(value_type)

def get_type_name(value_type, with_size = False):
Get the property type as name string.
if value_type in value_types:
if with_size:
return '%s (%d bytes)' % (value_types[value_type], get_type_size(value_type))
return '%s' % value_types[value_type]

def get_values_as(resource, item_index, value_type):
Get the resources values of the item at the given index.
If the requested type doesn't match with the actual property type an empty value list is returned,

if value_type == ix.api.ResourceProperty.TYPE_CHAR:
return resource.get_string(item_index)

if value_type == ix.api.ResourceProperty.TYPE_INT_8 or value_type == ix.api.ResourceProperty.TYPE_UINT_8:
return resource.get_byte(item_index)

if value_type == ix.api.ResourceProperty.TYPE_INT_16 or value_type == ix.api.ResourceProperty.TYPE_UINT_16:
return resource.get_short(item_index)

if value_type == ix.api.ResourceProperty.TYPE_INT_32 or value_type == ix.api.ResourceProperty.TYPE_UINT_32:
return resource.get_int(item_index)

if value_type == ix.api.ResourceProperty.TYPE_INT_64 or value_type == ix.api.ResourceProperty.TYPE_UINT_64:
return resource.get_long(item_index)

elif value_type == ix.api.ResourceProperty.TYPE_FLOAT_32:
return resource.get_float(item_index)

elif value_type == ix.api.ResourceProperty.TYPE_FLOAT_64:
return resource.get_double(item_index)

ix.log_warning('get_values_as: unsupported type "%s"' % get_type_name(value_type))
return []

# -------------------------------------------------------------------
# Functions to create dummy matrix data and convert it to string.

def create_dummy_m44d(random_values = False, transpose = True):
Create a GMathMatrix4x4d with values for the sake of testing.
if random_values:
# matrix with random values
m44d = ix.api.GMathMatrix4x4d()
for i in range(4):
for j in range(4):
m44d.set_item(i, j, random.random())
# identity matrix
m44d = ix.api.GMathMatrix4x4d(True)

# to be in Alembic format the matrix must be transposed
if transpose:

return m44d

def m44d_to_string(m44d, separator = ' '):
Get the GMathMatrix4x4d flattened values as a string separated with the separator.
s = ''
for i in range(4):
for j in range(4):
s += '%f' % m44d.get_item(i, j)
if j < 3: s += separator
if i < 3: s += separator
return s

# -------------------------------------------------------------------

def get_point_property(particle_object, prop_name):
Get a GeometryPointProperty from a GeometryParticle object.

prop_collection = particle_object.get_module().get_properties()
if not prop_collection:
ix.log_warning('There are no properties.')
return None

prop_index = prop_collection.get_property_index(prop_name)
if prop_index == INVALID_INDEX:
ix.log_warning('Property "' + prop_name + '" not found.')
return None

prop = prop_collection.get_property(prop_index)
if not prop:
ix.log_warning('Failed to retrieve property "' + prop_name + '".')
return None

return prop

# -------------------------------------------------------------------

def get_point_property_indices(particle_object, prop_name):
Get the GeometryPointProperty indices.
Returns None if the property isn't found or isn't indexed.

prop = get_point_property(particle_object, prop_name)
if not prop:
return None

if not prop.has_indices():
return None

return prop.get_indices()

# -------------------------------------------------------------------

def print_point_property_indices(particle_object, prop_name):
Get the value indices of the GeometryPointProperty.

indices = get_point_property_indices(particle_object, prop_name)
if indices:
s = []
for i in indices:
print 'Value indices for %s: index count = %d, indices = %r' % (prop_name, indices.get_count(), s)

# -------------------------------------------------------------------

def create_point_property(particle_container_object, prop_name, value_type, dimension):
Create a GeometryPointProperty in a GeometryParticleContainer with no values.
If the property already exists it is overwritten.

Known bug in 3.6 SP8: the Property Editor doesn't refresh after a property has been created by IOHelpers.

The GeometryParticleContainer object.
Property name.
Property value type (see ResourceProperty.Type).
Property dimension: number of data items per value.
- 1 vector3 value has 3 data items -> dimension = 3
- 1 matrix44 value has 16 data items -> dimension = 16

True if succeeded, False otherwise.

print 'Creating property "%s.%s" ...' % (particle_container_object.get_name(), prop_name)
return ix.api.IOHelpers.create_particles_property(particle_container_object, prop_name, value_type, dimension, '')

# -------------------------------------------------------------------

def set_point_property_values(particle_container_object, prop_name, values):
Set the values of a GeometryPointProperty in a GeometryParticleContainer.

This implementation might be limited if the values array is too big.
It could be possible to rework to add a parameter containing a list of point
indices along with the associated values, so that the values can be set by chunks.

The GeometryParticleContainer object.
Property name.
Python string array containg 1 string value per point.
Each string must contain as many values (separated by spaces) as the dimension of the property.
If there are too many values the excess values are ignored.
If there are not enough values, some points won't have values.
- to set the values of 2 x vec2f to (1.0, 2.0) and (3.0 4.0), the python string array is ["1.0 2.0", "3.0 4.0"].

True if all values were set, False if at least one value failed to be set.


print 'Setting values in property "%s.%s" ...' % (particle_container_object.get_name(), prop_name)

point_count = int(particle_container_object.get_module().get_point_count())
values_used = min(point_count, len(values))
if len(values) != point_count:
ix.log_warning('Value count (%d) and point count (%d) differ: only %d values will be used.' % (point_count, len(values), values_used))

# NOTE: this function would require some reworking to use indexed values in order to reduce the number of stored values, to share
# values between points. For example you can have N values, and assign those N values to M points, where M = point_count and N <= M.
# - point 0 -> assign value at index 0
# - point 1 -> assign value at index 0
# - point 2 -> assign value at index 2
# ...
# - point M-1 -> assign value at index 1

# assign each value to each point
all_ok = False
for i in range(values_used):
# create a new value index for the new value
new_indices = ix.api.ULongSet()
ok = ix.api.IOHelpers.edit_particles_property(particle_container_object, prop_name, new_indices, ix.api.IOHelpers.EDIT_PROPERTY_MODE_SET, values[i])
if not ok:
ix.log_warning('Failed to set value for point %d.' % i)
all_ok = all_ok and ok

return all_ok

# -------------------------------------------------------------------

def print_property_values(particle_container_object, prop_name, value_type):
Print the values of a GeometryPointProperty in a GeometryParticleContainer.

print 'Printing property "%s.%s" ...' % (particle_container_object.get_name(), prop_name)

module = particle_container_object.get_module()
point_count = int(module.get_point_count())
log_debug('point count = %d' % point_count)

# get the property collection
prop_collection = module.get_properties()
if not prop_collection:
ix.log_warning('No properties.')

# get the property index
prop_index = prop_collection.get_property_index(prop_name)
if prop_index == INVALID_INDEX:
ix.log_warning('Property "' + prop_name + '" not found.')

# get the property
prop = prop_collection.get_property(prop_index)
if not prop:
ix.log_warning('Failed to get property "%s" at index %d.' % (prop_name, prop_index))

value_type = prop.get_value_type()

# debug stuff
value_count = prop.get_value_count() # number of property values
value_extent = prop.get_value_extent() # number of value item per per property value (e.g. vec2 has extent = 2)
index_count = prop.get_index_count()
time_sampling = prop.get_time_sampling()
sample_count = time_sampling.get_sample_count()
log_debug('value_type = %d (%s)' % (value_type, get_type_name(value_type)))
log_debug('value_count = %d' % value_count) # number of values: 2 x vec3f -> 2
log_debug('value_extent = %d' % value_extent) # number of value "items" per value: vec3f -> 3
log_debug('index_count = %d' % index_count) # number of indices: usually same as number of points
log_debug('sample_count = %d' % sample_count) # number of samples per value
log_debug('has_indices = %r' % prop.has_indices())

# get the ResourceProperty for the 1st sample (0)
resource = prop.get_values_property(0)

# debug stuff
log_debug('resource.value_size = %r' % resource.get_value_size()) # size of 1 value item: vec3f -> float32 = 4 bytes
log_debug('resource.item_count = %r' % resource.get_item_count()) # number of values (== prop.value_count)
log_debug('resource.value_count = %r' % resource.get_value_count()) # total number of value items: 2 x vec3f -> 2 x 3 = 6
log_debug('resource.item_value_count = %r' % resource.get_item_value_count(0)) # number of items per value: vec3f -> 3

# iterate over the points
for item_index in range(resource.get_item_count()):
cur_values = get_values_as(resource, item_index, value_type)

log_debug('len(cur_values) = %r' % len(cur_values))
log_debug('cur_values = %r' % cur_values)

if len(cur_values) == 0:

cur_extent = resource.get_item_value_count(item_index)
log_debug('cur_extent = %d' % cur_extent)

log = 'value %d = ' % item_index
if value_type == ix.api.ResourceProperty.TYPE_CHAR:
log += cur_values[:cur_extent]
for i in range(cur_extent):
log += '%r ' % cur_values[i]
print log

# -------------------------------------------------------------------
# Run it !

# get the particle container and its point count
container = ix.get_item("project://scene/context/container")
point_count = int(container.get_module().get_point_count())

# ----- create a matrix44d property (dim 16) -----

# create as many matrices as points in the particle container, and put the values in a string array
m44d_values = []
for i in range(point_count):
m44d = create_dummy_m44d(True)

prop_name = 'm44_float64'
create_point_property(container, prop_name, ix.api.ResourceProperty.TYPE_FLOAT_64, 16)
set_point_property_values(container, prop_name, m44d_values)
print_property_values(container, prop_name, ix.api.ResourceProperty.TYPE_FLOAT_64)

# ----- create an int32 property (dim 1) -----
int32_values = []
for i in range(point_count):

prop_name = 'int32'
create_point_property(container, prop_name, ix.api.ResourceProperty.TYPE_INT_32, 1)
set_point_property_values(container, prop_name, int32_values)
print_property_values(container, prop_name, ix.api.ResourceProperty.TYPE_INT_32)

# ----- create a string property (dim 1) -----
text_values = []
for i in range(point_count):
text_values.append('text_%d' % i)

prop_name = 'text'
create_point_property(container, prop_name, ix.api.ResourceProperty.TYPE_CHAR, 1)
set_point_property_values(container, prop_name, text_values)
print_property_values(container, prop_name, ix.api.ResourceProperty.TYPE_CHAR)
(13.68 KiB) Downloaded 2 times
(36.88 KiB) Downloaded 3 times
Anthony Nemoff
R&D Engineer
User avatar
Posts: 87
Joined: Wed Jan 13, 2016 10:10 am

Return to Scripting