Isotropix Forums

General python data collecting questions

Clarisse Scripting related topics

General python data collecting questions

Unread postby DarkNemos » Thu Jun 28, 2018 10:11 pm

Hi guys,

I have a few general questions about writing python in clarisse that i would like to clarify. I understand that clarisse is using a C++ python wrapper in order to manipulate data, hence all the data structures are C++ like. My knowledge of C++ is on a very basic level but i do understand arrays and how they work on some level.

What is most confusing for me is how to get elements based on a certain criteria. When i say elements i mean anything that you could find in the clarisse scene.

Finding an object based on a name seems to be pretty easy. For example:

objects = ix.api.OfObjectVector() # not sure but i think it creates an empty object of type vector?
ix.application.get_matching_objects(objects, "*", "Light") # fills in the object with the objects matching

The problem for me starts when i want to find an object that belongs to something. For example i want to get all the objects that are in a 3d layer?

It seems to me that the code is different if you want to find:

- all objects connected to an image
- all objects connected to a layer 3d
- all objects in a group
- all object in a context
- all shaders connected to a shader group

... and so on.

Do i have to write recursive functions to walk through step by step collecting data or is there a more elegant way?

Can somebody please point me to the logic of searching and finding the right data in the right way?

Also i could not find anything in the documentation regarding what is: ix.api.OfContextSet(), OfContextArray(), OfObjectSet(), OfObjectArray() and so on. And also how to add to them.

When i want to manipulate a layer for example do i work with the object or do i work with the module?

Sorry if it was too many questions, i am just not used to writting code in a clarisse way :)

Thanks !
DarkNemos
 
Posts: 5
Joined: Sun Nov 19, 2017 3:16 am

Re: General python data collecting questions

Unread postby bvaldes » Mon Jul 09, 2018 1:27 pm

Hi,

There is different way to do depending on what you are looking for. A good start is to have a good understanding on how Clarisse works first.
I will try to answer at your question one by one.

1 - all objects connected to an image / all objects connected to a layer 3d
To get the connection (input or output), there is a menu for that: Edit > Select > Input/output recursively
All the menu in clarisse are written in python except windows. You can find the script in ClarisseInstall/python/menus/main_menu
Take a look to this example:

python code

# get the image
image = ix.get_item("project://scene/image")
# To get the inputs of this image, you have to select it
ix.selection.select(str(image)) # This function need an string not a Clarisse item, so you have to convert the item to string
ix.application.select_all_dependencies() # select all the connections to this node

# Now we have to deal with the selection
for i in range(ix,selection.get_count())
print ix.selection[i] # display the name of the object at the position `i` in the selection list

2 - all objects in a group
The result of a group is contained in the `references` attribute. So in python we have to get the content of this attribute to get the content of the group.

python code

# Get the group
group = ix.get_item("project://scene/group")

# Get the attribute that contain the result objects of a group
result_attr = group.get_attribute("references")
for i in range(result_attr.get_value_count()): # result_attr.get_value_count() correspond to the number of items in the result list
print result_attr.get_object(i) # display the name of the object at the position `i` in the selection list

3 - all objects in a context

python code

# Get the context
ctx = ix.get_item("project://scene")

# We have to create some arrays that will content the objects in the context
objects_array = ix.api.OfObjectArray(ctx.get_object_count()) # ctx.get_object_count() correspond to the number of items in the context

ctx.get_objects(objects_array) # this function get all the items in the context and fill the array `objects_array` of them

for i in range(ctx.get_object_count()):
print objects_array[i]

This script is quite simple and allow you to get only the first level. If you want it recursive, you can use the following function:

python code

def getSubContext(context = ix.get_current_context(), recursive = True):
result = []
context = ix.get_item(str(context))
sub_contexts = []
for i in range(context.get_context_count()):
result.append(context.get_context(i))
sub_contexts.append(context.get_context(i))
if recursive:
while len(sub_contexts)>0:
workOn = sub_contexts
sub_contexts = []
for context in workOn:
for i in range(context.get_context_count()):
sub_contexts.append(context.get_context(i))
result.append(context.get_context(i))
return result

def getItems(context = ix.get_current_context(), kind = None, recursive=True):
result = []
workOn = [ix.get_item(str(context))]
vItems = ix.api.OfItemVector()
if recursive:
for subContext in getSubContext(workOn[0], recursive = True):
workOn.append(subContext)

for context in workOn:
if (context.get_object_count()):
objects_array = ix.api.OfObjectArray(context.get_object_count())
context.get_objects(objects_array)
for i_obj in range(context.get_object_count()):
if kind is not None:
if objects_array[i_obj].is_kindof(kind):
vItems.add(objects_array[i_obj])
else:
vItems.add(objects_array[i_obj])

for item in vItems:
result.append(item.get_full_name())
return result

print getItems("project://scene") # display the list of all items in the context give as parameter and its subcontext


4 - all shaders connected to a shader group
You can't connect several shaders to a single shading group. The rule is simple: 1 shading group => 1 shader.
In Clarisse the shaders are listed in the attribute materials of an item. If the item has several shading group, it has the corresponding number of entry in the materials attribute. The materials attribute is hidden to be more clear in the Attribute editor and avoid user to do things that he shouldn't :D . Moreover, in python you don't need to see something to access it:

python code

box = ix.cmds.CreateObject("box", "GeometryBox") # Create a box
# The module of a Clarisse item is the brain of the item. It contain all the logic of the item.
# If you don't get the module, you are only working on the interface (attributes for example)
boxModule = box.get_module()

for i in range(boxModule.get_shading_group_count()): # get_shading_group_count() gives the number of shading groups of the item
# boxModule.get_shading_group(i) is the shading group at the position `i`
# the attribute materials of the box is invisible and contain the list of all materials applied to the item (each material has an index to correspond to a shading group)
print boxModule.get_shading_group(i) + ":" + str(box.get_attribute("materials").get_object(i)) # display the name of a SG and the material applied to it



As you see there isn't one answer for all these questions. The forum is here for that too, when you are stuck in a problem, you can always ask.
I hope that I answered to your question, if you need more info, feel free to ask

Best regards,
Benoit
Benoit VALDES
Isotropix
Clarisse QA
User avatar
bvaldes
 
Posts: 280
Joined: Mon Sep 26, 2016 10:44 am

Re: General python data collecting questions

Unread postby DarkNemos » Wed Oct 24, 2018 8:27 pm

Hi Benoit,

Thank you for your detailed explanation. The reply you provided was very useful.

I do have some more questions.

Currently i have a function that reorders all contexts in the scene alphabetically witch works great but its a bit slow. The main slowdown comes from the fact that in order to reorder them i move all in a temp context and then i move them back in order.

Here is some profiling info:

Code: Select all
         5788 function calls (5741 primitive calls) in 25.276 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       48    0.001    0.000    3.000    0.063 :3(CreateContext)
       48    0.001    0.000    4.842    0.101 :3(DeleteItems)
      170    0.002    0.000   17.410    0.102 :3(MoveItemsTo)
      ...


ix.cmds.MoveItemsTo() seems to be taking most of the time. Is there a better way of doing this? Could i use Cut(), Copy(), Paste() commands to serialize/deserialize content faster? I was unable to figure out the right arguments for serialize/deserialize functions.
Interesting bit in the profiling is that deleting a context takes more time than creating one. Almost double.

Second question is: can you influence through python the expansion and colapsing the contexts hierarchy in browser and explorer?

Please let me know.

Thanks!
DarkNemos
 
Posts: 5
Joined: Sun Nov 19, 2017 3:16 am

Re: General python data collecting questions

Unread postby bvaldes » Thu Oct 25, 2018 8:40 am

Hello,

I think that you could save lots of time if you MoveItems() from the source context to a temp one and then delete the source context and rename the temp one with the name of the source. Like that you will do only one move per item.

About the copy/past, you can do it by script but this is not a good idea. You will loose the connection between items that were not been copied. Let me explain with an example:
Let's say that you ave 2 context: A and B. In context A there is a material. In context B there is a Box. The material is assigned to the box. If you copy context A to context C for example and then delete the A. The box will not have the material anymore. You have to copy both context A and B to keep the link between the materials.

python code

ix.application.copy() # to copy the thing that are selected
ix.application.paste() # paste in the current context

About the way to expand or collapse a context in browser/explorer, there is no real way to do that. This is GUI only. But by setting the current context, you will open directly in the browser the context you are interested with.

python code

ix.set_current_context("project://context") # The argument is the context you want to set as current

One last thing: You can reorder the browser by clicking on the "Name" header in it. By default the order depend on creation but if you click on the "Name" header, you will be able to reorder by name ascending and descending. I don't know if this is enough for you but that could work.

I hope that could help you. Best regards,
Benoit VALDES
Isotropix
Clarisse QA
User avatar
bvaldes
 
Posts: 280
Joined: Mon Sep 26, 2016 10:44 am


Return to Scripting