# Constrained Axis

Without (left) and with (right) constrained axis.

The ability to constrain a haptic device to certain directions is important, especially in the realm of surgery where mistakes can be tragic. This can be accomplished using H3D force effects and a Python script. In order to activate the constraint only when we want, we will set the main button on the haptics device as a trigger.

A video of this tutorial can be found here. Note this tutorial does not go over the application of the paint layer. This is talked about here.

There are multiple parts to this: (1) Importing information from your haptics device, (2) Setting up your scene, (3) setting up default constraints in X3D, and (4) creating a script to define the constrain.

## Importing Device Info

Getting information from the haptics device is a matter of one line of code. You must import the device and set it to a variable, in this case "HDEV."

```
<IMPORT inlineDEF="H3D_EXPORTS" exportedDEF="HDEV" AS="HDEV" />
```

## Scene

I will set up a basic sphere to help visualize the usefulness of constraining the axis. Based on my demonstrations it is more effective to have use a sphere than an object with sharp edges such as a cube. this is especially true when using a paint layer (as seen in the example video)

```
<Transform DEF="transform" translation = "0.0 0.0 0.0">
<Shape>
<Appearance>
<Material diffuseColor="1 0 0"/>
<SmoothSurface/>
</Appearance>
</Shape>
</Transform>
```

## Force Effects

The force effect is added using the H3D PositionFunctionEffect.The function is comprised of three equations. For sake of this tutorial we will only use one function. It is possible to set different buttons to activate constraints in different axis using the same principles.

The PositionFunctionEffect node takes in GeneralFunction objects. We will define them to use later with our Python script. The containerField takes in the enumerated value "xFunction," "yFunction," or "zFunction" to designate direction. The function is in the form of numbers and variables. For example you could write function="x+2y-4z." The function is parsed with the variables found in the params input. In our case we want to evaluate with params="x,y,z" in case we add non-linearity in the Python script. By default we are going to set the forces to "0." The following is an example:

```
<PositionFunctionEffect>
<GeneralFunction DEF="xFun" containerField="xFunction" function="0" params="x,y,z"/>
<GeneralFunction DEF="yFun" containerField="yFunction" function="0" params="x,y,z"/>
<GeneralFunction DEF="zFun" containerField="zFunction" function="0" params="x,y,z"/>
</PositionFunctionEffect>
```

## Routing

Next we must set up a link in the X3D file to the Python script. A more in-depth example can be found in this tutorial. We want to route the main button from the haptic device as well as the variable xFun from the GeneralFunction. This will update the function at every timestep.

```
<PythonScript DEF="Constrain" url="C:\H3D\CLEA\Line\line_restrict.py"/>

<ROUTE fromNode= "HDEV" fromField="mainButton" toNode="Restrict" toField="x_but"/>

<ROUTE fromNode="Constrain" fromField="x_button" toNode="xFun" toField="function"/>
```

Python Script The Python script is relatively simple. We want to set the value for the X-axis to be a function of the distance away from the center of the haptic device if the main button is depressed. Otherwise the function should be zero if the button is not being pressed.

We import the variables from X3D using:

```
#line_restrict.py
from H3DInterface import *
```

We then must create the action by creating a new class with an update function. The value of the button is set variable button using the routing function. We then simply use an if...else statement to return the expression we want to input to the positionFunctionEffect. The value must be in quotes.

```
#line_restrict.py
class X_fun( TypedField(SFString, SFBool)):
def update(self, event):
routes_in = self.getRoutesIn()
button = routes_in[0].getValue()
if(button):
return "-200*x"
else:
return "0"
```

By adding this function we are basically adding a virtual spring about the x-axis. The value of "x" is evaluated in the PositionFunctionEffect based on the position of the end effector. The farther you move the device away from the center the greater the forces get.

To apply the action, the class must be called using: </python>

```   #line_restrict.py
x_button = X_fun()
```

The value x_button is returned by the script to the scene as called for above.

## Code

The complete X3D file:

```
<Group>
<IMPORT inlineDEF="H3D_EXPORTS" exportedDEF="HDEV" AS="HDEV" />

<PositionFunctionEffect>
<GeneralFunction DEF="xFun" containerField="xFunction" function="0" params="x,y,z"/>
<GeneralFunction DEF="yFun" containerField="yFunction" function="0" params="x,y,z"/>
<GeneralFunction DEF="zFun" containerField="zFunction" function="0" params="x,y,z"/>
</PositionFunctionEffect>

<Transform DEF="transform" translation = "0.0 0.0 0.0">
<Shape>
<Appearance>
<Material diffuseColor="1 0 0"/>
<PaintableTexture DEF="TEXTURE" backgroundColor="0.5 0 0 1" paintColor="1 1 1 1"/>
<SmoothSurface/>
</Appearance>
</Shape>
</Transform>

<PythonScript DEF="Constrain" url="C:\H3D\CLEA\Line\line_restrict.py"/>
<ROUTE fromNode= "HDEV" fromField="mainButton" toNode="Constrain" toField="x_but"/>
<ROUTE fromNode="Constrain" fromField="x_but" toNode="xFun" toField="function"/>

</Group>
```

The complete Python script:

```
from H3DInterface import *

class X_fun( TypedField(SFString, SFBool)):
def update(self, event):
routes_in = self.getRoutesIn()
button = routes_in[0].getValue()
if(button):
return "-200*x"
else:
return "0"

x_but = X_fun()
```