Writing Python 3 Applications using Vortex Studio

 

This document describes how to write Python 3 applications using Vortex Studio SDK.

Python Usage and Environment Configuration

You can use your preferred integrated development environment (IDE) to work with Python.

The only restriction is that the supported Python version is Python 3.8. The default interpreter currently bundled with Vortex Studio is 3.8.6.

Please note that due to the way the Vortex Python module is generated from C++, automatic completion features of IDEs may not always work with some Vortex Objects.

Vortex Content might have been created within the Vortex Studio Editor or by code, be it C++ or even Python

To use Vortex SDK in Python, there is only one Python module: Vortex.py.

The module is located in C:\CM Labs\Vortex Studio #versionnumber\bin.

Once PYTHONPATHis updated with the path of Vortex.py, the developer simply needs to import the Vortex library.

  • Everything is Vortex in Python e.g. C++ VxContent::Scene becomes Vortex.Scene, VxMath::Matrix44 becomes Vortex.Matrix44 etc...

  • The command dir() or dir(object) will list the module content and the object methods.

  • Help(object) will provide more information on one object in particular.

  • Python HTML doc generation using pydocpython -m pydoc -w Vortex

  • Vortex Studio SDK container-like classes support len() and operator[]

Import Vortex
import Vortex   s = Vortex.Scene.create() m = Vortex.Matrix44()  r = len(m) # r = 4 c = len(m[0]) # c = 4

How to set PythonPath when using Vortex.py

PythonPath must be extended with the path pointing to the binaries of Vortex Studio.

The following are example on how to set PYTHONPATH with Vortex Studio :

  • open a command line and enter set PYTHONPATH=%VORTEXPATH%\bin;%PYTHONPATH% before starting python.exe

  • inside a python terminal, update sys.path import sys; sys.path.append('VORTEXPATH\bin')

  • inside VsCode, add the following line to the .env file PYTHONPATH=VORTEXPATH\bin

Please refer to the documentation of Python 3 to define the appropriate PYTHONPATH.

When using a VxApplication within a Python process and that application will be loading content that contains Dynamics Script Extension, the Interpreter Directory must be the same as the starting Python process.

import sys import Vortex myApp = Vortex.VxApplication() # Create a setup setup = Vortex.ApplicationConfig.create() #Set the interpreterDirectory to the same that launched this script setup.parameterPython3.interpreterDirectory.value = sys.exec_prefix # add other modules to setup ... setup.apply(myApp)

 

Vortex SDK - Python specificities

Property .value

In extensions, the inputs, outputs, and parameters are known as fields. 

Fields can be accessed in several ways:

  1. In a generic way, via methods getInput/getOutput/getParameter of any VxExtension. e.g. extension.getInput('Int').value

  2. In a specific way via the interface. e.g part.inputControlType.value

  3. In a specific way within a dynamics script extension, using extension. e.g. extension.inputs.myInt.value

The value of a Field can be read or written using the Python property "value", a generic accessor that implicitly performs type conversion, which was added in the Vortex Python API. 

If the field is an integer, the property "value" will be an integer. If the field is a reference to an Interface e.g Field<Assembly> interface, the property "value" will return that interface e.g. AssemblyInterface

.value Property

# The input is an int extension.getInput('Int').value = 8   # The output is a VxVector3, extract x from VxVector3 x = extension.getOutput('Vector3').value.x   # The Parameter is an Field<Assembly> extension.getParameter('Assembly').value.addPart(part)

Section Set/Get Values on Inputs, Outputs, and Parameters of a dynamics script extension below contains additional examples.

Setting value on vector using accessors

.value cannot be used in combination with any accessor to assigned a value. E.g., input.value.x = 1 won't work, one should use input.value = VxVector3(1,1,1)

Object Interfaces

The Vortex API exposes each Content Objects in Python using either their base type or their specialized types:

  • Base Content type objects: 

    • Scene -> SceneInterface

    • Configuration -> ConfigurationInterface

    • ConnectionContainer-> ConnectionContainerInterface

    • VxVHLInterface -> VxVHLInterfaceInterface

    • Mechanism -> MechanismInterface

    • Assembly -> AssemblyInterface

    • Part -> PartInterface

    • Constraint -> ConstraintInterface

    • CollisionGeometry -> CollisionGeometryInterface

  • Specialized Content objects that inherit from one of the base content types: 

    • prismatic constraint -> PrismaticInterface

    • hinge constraint -> HingeInterface

    • sphere cg -> SphereInterface

Converting Interfaces

Interface conversion is explicit. Base class objects can be converted to specialized objects using the right interface constructor.

Explicit Conversion

  • Base Constraints Interface (or VxExtension of constraints) can also be converted to the right Interface using Constraint.getConstraintInterface(constraint)

  • Base Collision Geometry (or VxExtension of collision geometry) can also be converted to the right Interface using CollisionGeometry.getCollisionGeometryInterface(cg)

Most content objects have an interface version in Python. When they are not available, just use the VxExtensionFactory and work with the VxExtension in a generic way.

Custom Interfaces developed by clients will not have Python Interface objects.

code snippet - creating an object

IMobile and transformation Matrix

Content objects that can be moved are derived from the IMobile interface. IMobileInterface objects can be moved with using method setLocalTransform, which takes Vortex.Matrix44 object describing the transformation in terms of scale, rotation and translation.

IMobile

VxMath::Transformation

A series of global helpers exist at the Vortex level to simplify matrix computation. They are the equivalent of the C++ Global helpers in the namespace VxMath::Transformation.

Transformation helpers

Accessing Application Context

To get information relative to the application context, use getApplicationContext(). The method is available via any VxExtension or extension in the case of a dynamics script extension. Application context API is relatively simple and can be used for time/frame-based related logic. Application context methods are also available directly from the VxApplication . e.g.  <application_obj>.getSimulationFrameRate(), although the context itself can otherwise be accessed from a VxApplication using method getContext().

  • <obj>.getApplicationContext().getSimulationFrameRate() # Frame rate e.g. 60 means 60 fps

  • <obj>.getApplicationContext().getSimulationTimeStep() # Time of a step. Is the inverse of frame rate e.g. Frame Rate = 60, Time Step = 1/60 = 0.016666

  • <obj>.getApplicationContext().getSimulationTime() # Current Time of the simulation. Time increases by Time step every frame.

  • <obj>.getApplicationContext().getFrame() # Current Frame of the simulation

  • <obj>.getApplicationContext().getApplicationMode() # Current Simulation mode i.e. KEditingMode, kSimulatingMode or kPlaybackMode

  • <obj>.getApplicationContext().isPaused() # Indicates if the simulation is paused

  • <obj>.getApplicationContext().isSimulationRunning() # Indicates if the Simulation is running i.e. ApplicationMode is kSimulatingMode And isPaused == False

Application Scripting

Python can be used for prototyping, tests (some of Vortex internal tests are written with Python), or a complete Vortex Application.

The concept is no different than what is described in the section Making a Vortex Application. The difference is that the application is started in Python.

Creating a VxApplication

The first step is to create an application and set it up. Use the Vortex Studio Editor to create its Application Setup. Then it is simply a matter of loading the setup to apply it.

Setting up an application

Note that like in C++, it is possible to insert and remove modules and extensions manually to the application as well as set up some application parameters.

Likewise, it is possible to create an ApplicationConfig object in Python. However, not all modules and extensions factory keys and the VxID to its parameters are exposed in Python.
Although it is technically possible to create a valid application setup in Python, the user is encouraged to use the Vortex Studio Editor to create its Application Setup.

Application Modes

Application mode is part of the application context and is used by Modules and Extensions to make some decisions. Modules and extensions are aware of what the application is doing and can adapt their behavior.

There are 3 application modes in Vortex :

  • Editing mode: The simulation is not computed at every step. That's when a user usually changes an object's parameters, move objects, add or remove objects, activate a configuration, load content, etc.

  • Simulating mode: The simulation is computed at every step, objects parameters should not be modified.  The simulation can be paused and resumed.

  • Playback mode: The simulation is not computed, objects are moved from the playback source so that content is replayed “graphically”.

The Editor starts in editing mode. The user edits his content. When the user hits the play button, it goes in simulating mode. On stops, it goes back to editing. 

The Player also starts in editing mode. The user selects his content. When the user hits the play button, it goes in simulating mode. On stop, it goes back to editing. If the user loads a recording, the player goes in playback mode.

The default mode for a Vortex Application is simulating, it can be set to editing in the Application Setup.

Changing the mode is not instantaneous and usually requires at least 1 application update.

To change the mode manually, the best way is to use the vxatp helper VxATPUtils.requestApplicationModeChangeAndWait(application, mode), where mode is either kModeEditing or kModeSimulating.

Example:Application Mode

Pausing the simulation

If the mode is set to simulation, the user can pause the simulation by calling pause().

Simulation can be resumed be calling resume().

Loading Content

Loading content created using the Vortex Studio Editor should be done with the VxSimulationFileManager provided by the VxApplication. The object loaded is distributed across the network. Object loaded this way is not meant to be edited, most changes will not be distributed. Content should be loaded while the application is in editing mode.

To edit content using Python, see section Content Creation.

Content gets unloaded on its own when the application is destroyed, but should you need to remove content, the simulation file manager can also be used to remove content via unloadObject().

Example:Loading a scene



Running the Application

Once the application has some content loaded or created, it can be updated.

If you only need to run the application until it ends, use the function run()Function run() on the application is basically a loop that calls update() continuously. It could be replaced with the code below.

run()

Depending on the mode, the physics will be simulated or not when there is an update.
For example, in the Vortex Editor, the default mode is editing. Once the user clicks on the start button, the application goes to simulating. On clicking the stop buttons, it goes back to editing. There are always calls to update().

Function update() must be called once per frame, no matter the simulation mode and also when the simulation is paused, as modules require updates no matter the simulation mode or the pause state. The application context is available like in C++ and the mode should be set properly depending on what you are doing, 


If you need to perform things outside the update, such as inspecting content, or run a specific number of frames, rather than call run(), call update().

running 100 frames

Inspecting and updating content

To look into content, a user can browse from the VxObject returned by the simulation file manager. It is possible to use <content_object>Interface to get to the object of interest, as explained in the Content Creation in Python section above.

The following is an example of how to use a hinge constraint retrieved from a scene object in Python:

Browsing content

Once the references to the required objects have been established, the external application basically need to update the inputs, execute an update and read the values from the outputs. Data should not be written into outputs. Parameters value should not change during simulation. In Python, when you have access to the interface API, it is preferable to use it to get access to the fields rather than using function getInput()/getOutput()/getParameter() on the VxExtension, as the second option requires knowledge of the field's VxID and is less efficient.

Getting data from content

Using Keyframes to restore the initial positions

Keyframes can be used to restore the values of content objects taken at some point in time, e.g. when starting the simulation.

The first thing to do is to create a keyframe list and then use it to save and restore keyframes.

The following provides an example of saving one keyframe and restoring it later.

Save and Restore Keyframe

In contrary to the C++ implementation, there is no callback implemented to know when a keyframe is ready, therefore a small pull function must be implemented

waitForNbKeyFrames

Content Creation

Content can also be created, or updated after being loaded.

Please read Creating Content for the fundamentals of content creation. The concepts described in that section apply in Python as well and will not be repeated here.

About Extensions and factory keys

Not all extensions and module factory keys are exposed directly via the python API, only the most commonly used. Should you need to invoke the extension factory with a specific factory key that you don't know, it is possible to invoke the extension factory to get the list of all the available factory keys. 

The following provides an example of how to access the information of a Factory Key from the list fetched.

code snippet - Extensions factory key with an object

 

Working with definitions and instances

Use the VxObjectSerializer to load and save your document definition. Children in documents are instances and must be instantiated like in C++.

code snippet - I/O with an object

Clone, instantiate and sync

The C++ global functions to perform those operations are not available in Python. However, all Python Smart Interfaces have additional functions that their C++ counterparts do not have: clone, instantiate and sync

code snippet - sync operations

Content Creation examples

These examples reproduce the C++ examples found in Creating Content

See Python tutorial Content Creation for additional examples.

Collision Geometries

Collision Geometries

Parts

Part

Assemblies

Assemblies

Constraints

Constraints

Attachments

Attachments

Mechanisms

Mechanisms

Scenes

Scenes

VHL Interface

VHL

Connection Container

Connections Container

Dynamics Script Extension (Python 3)

Python extensions do not have a dedicated interface, so a VxExtension is used directly.

Python Extension

Configuration

Configuration can also be edited in Python, but it is preferable to use the Vortex Studio Editor, as it is easy to make mistakes in code. Configurations can only be activated while the application is in editing mode,

Configuration

Vortex Automated Test Platform

A module containing tools to write tests in Python with Vortex is made available in the binary directory.

This module is called vxatp3.

Structure of vxatp3 folder

vxatp3 has the following directory structure:

  • vortex \bin\vxatp3 : the Python package

  • vortex \bin\vxatp3_*.bat : batch helpers to run tests from the command line

  • vortex \resources\vxatp\ : resources used by vxatp3, e.g. setup documents

Setting the environment to run vxatp3

The batch command vxatp3_set_env.bat details how to properly set the environment when starting from the binary installation directory.

vxatp_set_env

Any IDE or Python interpreter would have to set up or inherit the same environment.

How to script a test

The vxatp3 test scripts are similar to standard scripts using the python package unittest: unittest#basic-example.

From the configuration given, a VxApplication can be created to perform operations on assets.

A helper is provided in VxATPConfig that return a setup application ready to be tested : VxATPConfig.createApplication(self, prefix_name, config_name).

In addition, VxATPConfig.createApplication sets to the test case a configuration member that contains useful information in the context of vxatp3.

The configuration can be accessed with self._config .

The configuration contains the following:

  • self._config.app_config_file : the setup file. It can be overridden by the test itself to add or remove specific vortex modules.

  • self._config.app_log_prefix : the prefix for the log file. It can be overridden by the test to specify the name of the test case in it.

  • self._config.output_directory : the output directory desired by the test suite. It should be used by the test to output data in a safe place (SVN and other versioned locations must be avoided).

Vortex Python module must also be imported in order to use the Vortex Python binding of the SDK.

Example from unittest documentation adapted to vxatp3

Helpers for default setups

VxATPConfig provides optional helpers to get default predefined setup of the application :

  • VxATPConfig.getAppConfigWithoutGraphics() return absolute path to default config not containing graphics module

  • VxATPConfig.getAppConfigWithGraphics() return absolute path to default config containing graphics module

Helper to change application mode

  • VxATPUtils.requestApplicationModeChangeAndWait(self.application, Vortex.kModeEditing) Sets the new mode and wait for the change to be effective. Use to switch between kModeEditing and kModeSimulating.

Helper for tests parametrization

vxatp3 provides a decorator that allow tests to be parametrized.

The following gives an example of its usage.

Example using VxATPUtils.parametrize

The following is the output generated by the test.

example of parametrized test output

How to call a vxatp3 test from the command line

There are two ways a vxatp3 test can be called from the command line :

Calling a vxatp3 test like any unittest.TestCase

See unittest#command-line-interface (e.g. python -m unittest test_my_test.py).

A convenience batch file is also provided to set up all Vortex required environment variables.

running one vxatp3 test script

Calling vxatp3 test using vxatp_launcher

vxatp_launcher can be used to run all tests in a given directory or recursively or interactively

running all vxatp3 test scripts in a given directory

running all vxatp3 test scripts recursively

running one vxatp3 test script interactively

running all vxatp3 test scripts recursively with forcing graphics

 

In addition to running all test cases, the runner outputs xml formatted logs that can be used by any JUnit compatible parser.

example of console output

an example of xml output

More batch helpers

As a convenience, the following batch files are provided in the bin directory

set environment variables to use vxatp_launcher.py

running one vxatp3 test script

running all vxatp3 test scripts recursively from the test_directory

running one vxatp3 test script interactively discovered recursively from the test_directory

running one vxatp3 test script with graphics discovered recursively from the test_directory

How to use vxatp3 in Visual Studio 2015

Visual Studio has Python tools available as an add-on.

It includes among others functionalities an editor and a debugger.

To use it with vxatp3, you need first to configure your Python environment to be compatible with Vortex.

Next, in your Python project properties the 'Working Directory' must be the binary directory of your Vortex installation.

To be able to run and debug, the 'Search Paths' of the 'Debug' property tab must contain the binary directory of your Vortex installation, the vxatp3 path of your binary installation, and optionally the path containing your test scripts.

The settings should be enough to run and debug your vxatp3 based verification scripts in Visual Studio.

Python Tutorials

See Python Tutorials for the list of tutorials

Advanced Python Scripting

C++ Smart Interface vs. Python

Most of the C++ interfaces have their equivalent in the Vortex Python API.

C++ Smart Interfaces are IExtension's implementation with their VxExtension underneath, wrapped into a nice template class and is the preferred way of working with Vortex objects, rather than using VxExtension in a generic way, which requires the user to know every fields' Id.

In Python, it is still possible to work with VxExtension, like in C++. They can be created via the VxExtensionFactory as usual and still requires the knowledge of the field's id.

In order to use an equivalent of a C++ smart interface in Python, the Python API has objects named <content_object>Interface, where <content_object> is the name of the C++ interface. e.g. PartInterface Python object is the C++ equivalent of VxSmartInterface<Part>.

To use content objects in Python:

  • Methods are accessed via <content_object>Interface.<interface_methods>, e.g. mechanism.getExtensions() where mechanism is a MechanismInterface Python object (C++ equivalent of VxSmartInterface<Mechanism>).

  • <content_object>Interface.getObject() returns a VxObject typed instance, none is returned if it is not a VxObject.

  • <content_object>Interface.getExtension() returns the VxExtension typed instance

  • Can be created by calling <content_object>Interface.create(), or by constructing an <content_object>Interface from a VxExtension

Interface conversion

Since all objects are IExtension or IObject, all objects in Python are IExtensionInterface and IObjectInterface. However, passing from one to another, requires an explicit conversion.

Given object a of type <content_object>InterfaceA, to get an object b of type <content_object>InterfaceB : 

  • b = <content_object>InterfaceB ( a.getExtension() ) e.g., ConstraintInterface to HingeInterface or IExtensionInterface object to ConnectionContainerInterface.

Interface conversion

Note that while in C++ many functions (such as VxApplication::add() ) that take VxSmartInterface in a generic way work, because in C++ the conversion is implicit, it won't work in Python because conversion is explicit. Some of the Python API was extended to accept VxExtension to simplify general usage. The user simply needs to call getExtension() on the object interface when working in Python.

getExtension()