FANUC controllers give orientations as W, P, R angles, in degrees: rotation W around X, then P around Y, then R around Z, all around the axes of the reference frame. `XYZWPRPosition` gives conversions to quaternions and the usual frame operations. These functions work offline and can be used with any protocol of the SDK.

![W, P and R: rotations around the X, Y and Z axes of the reference frame.](/fanuc/documentation/diagrams/motion-wpr.svg)

## Quaternions

Quaternions are easier than angles to interpolate and to compare orientations.

**C# : MotionFramesOrientationsQuaternion**
```csharp
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Motion;
using UnderAutomation.Fanuc.StreamMotion.Data;

public class MotionFramesOrientationsQuaternion
{
    static void Main()
    {

        /**/
        var position = new XYZWPRPosition(500, 0, 300, 180, 0, 45);

        // W, P, R angles to quaternion, and back
        Quaternion q = position.GetQuaternion();
        position.SetQuaternion(Quaternion.FromAxisAngle(0, 0, 1, 90));   // 90 degrees around Z

        // Interpolation and angle between two orientations
        Quaternion a = new XYZWPRPosition(0, 0, 0, 180, 0, 0).GetQuaternion();
        Quaternion b = new XYZWPRPosition(0, 0, 0, 180, 30, 0).GetQuaternion();
        Quaternion halfWay = Quaternion.Slerp(a, b, 0.5);
        double angle = a.AngleTo(b);                // 30 degrees
        double[] axisAngle = halfWay.ToAxisAngle(); // x, y, z, angle in degrees
        /**/
    }
}
```

**Python : MotionFramesOrientationsQuaternion**
```python
from underautomation.fanuc.common.quaternion import Quaternion
from underautomation.fanuc.common.xyzwpr_position import XYZWPRPosition


##
position = XYZWPRPosition(500, 0, 300, 180, 0, 45)

# W, P, R angles to quaternion, and back
q = position.get_quaternion()
position.set_quaternion(Quaternion.from_axis_angle(0, 0, 1, 90))   # 90 degrees around Z

# Interpolation and angle between two orientations
a = XYZWPRPosition(0, 0, 0, 180, 0, 0).get_quaternion()
b = XYZWPRPosition(0, 0, 0, 180, 30, 0).get_quaternion()
half_way = Quaternion.slerp(a, b, 0.5)
angle = a.angle_to(b)                # 30 degrees
axis_angle = half_way.to_axis_angle()  # x, y, z, angle in degrees
##
```

- `GetQuaternion()` and `SetQuaternion()` convert between W, P, R and a quaternion (`Qw`, `Qx`, `Qy`, `Qz`).
- `Quaternion.Slerp()` interpolates on the shortest way between two orientations.
- `AngleTo()` gives the angle between two orientations, in degrees.
- `FromAxisAngle()`, `ToAxisAngle()`, `FromRotationMatrix()` and `ToRotationMatrix()` convert to and from other representations.

## Change of frame

A position is a frame: its X, Y, Z give the origin and its W, P, R the orientation. The same functions work for the positions of the robot, tool frames and user frames.

**C# : MotionFramesOrientationsFrames**
```csharp
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Motion;
using UnderAutomation.Fanuc.StreamMotion.Data;

public class MotionFramesOrientationsFrames
{
    static void Main()
    {

        /**/
        var tool = new XYZWPRPosition(0, 0, 150, 0, 0, 0);            // tool frame, relative to the flange
        var userFrame = new XYZWPRPosition(800, -200, 0, 0, 0, 90);    // user frame, relative to the world frame
        var flange = new XYZWPRPosition(700, 0, 400, 180, 0, 0);       // flange in the world frame

        // Flange <-> tool center point
        XYZWPRPosition tcp = flange.FlangeToTcp(tool);
        XYZWPRPosition flangeAgain = tcp.TcpToFlange(tool);

        // World frame <-> user frame
        XYZWPRPosition tcpInUserFrame = tcp.WorldToUserFrame(userFrame);
        XYZWPRPosition tcpInWorld = tcpInUserFrame.UserFrameToWorld(userFrame);

        // General frame operations
        XYZWPRPosition composed = userFrame.Multiply(tcpInUserFrame);   // same as tcpInWorld
        XYZWPRPosition inverse = userFrame.Inverse();
        double[,] matrix = flange.ToHomogeneousMatrix();                // 4 x 4
        /**/
    }
}
```

**Python : MotionFramesOrientationsFrames**
```python
from underautomation.fanuc.common.xyzwpr_position import XYZWPRPosition


##
tool = XYZWPRPosition(0, 0, 150, 0, 0, 0)            # tool frame, relative to the flange
user_frame = XYZWPRPosition(800, -200, 0, 0, 0, 90)  # user frame, relative to the world frame
flange = XYZWPRPosition(700, 0, 400, 180, 0, 0)      # flange in the world frame

# Flange <-> tool center point
tcp = flange.flange_to_tcp(tool)
flange_again = tcp.tcp_to_flange(tool)

# World frame <-> user frame
tcp_in_user_frame = tcp.world_to_user_frame(user_frame)
tcp_in_world = tcp_in_user_frame.user_frame_to_world(user_frame)

# General frame operations
composed = user_frame.multiply(tcp_in_user_frame)   # same as tcp_in_world
inverse = user_frame.inverse()
matrix = flange.to_homogeneous_matrix()             # 4 x 4
##
```

![World frame, user frame, flange and tool center point (TCP).](/fanuc/documentation/diagrams/motion-frames.svg)

| Need | Method |
| --- | --- |
| Position of the tool center point from the flange position | `flange.FlangeToTcp(tool)` |
| Flange position from the tool center point | `tcp.TcpToFlange(tool)` |
| Position in the world frame from a position in a user frame | `position.UserFrameToWorld(userFrame)` |
| Position in a user frame from a position in the world frame | `position.WorldToUserFrame(userFrame)` |
| Composition of two frames (A x B) | `a.Multiply(b)` |
| Inverse frame | `frame.Inverse()` |
| 4 x 4 homogeneous matrix | `position.ToHomogeneousMatrix()` |

The motion planner does these conversions for you when `ToolFrame` and `UserFrame` are set (see [Joint & Cartesian motions](/fanuc/documentation/motion-moves#tool_and_user_frames)).

## API reference

**Members of Common.Quaternion**
```csharp
public class Quaternion {
    // Creates the identity quaternion (no rotation)
    public Quaternion()

    // Creates a quaternion from its components
    public Quaternion(double qw, double qx, double qy, double qz)

    // Angle of the rotation between the two orientations, in degrees (0 to 180)
    public double AngleTo(Quaternion other)

    // Returns the conjugate of this quaternion. For a rotation, it is the inverse rotation.
    public Quaternion Conjugate()

    // Dot product of the two quaternions
    public double Dot(Quaternion other)

    public override bool Equals(object obj)

    // Creates a rotation around an axis
    public static Quaternion FromAxisAngle(double x, double y, double z, double angle)

    // Creates a quaternion from a rotation matrix (3x3, or 4x4 homogeneous matrix)
    public static Quaternion FromRotationMatrix(double[,] matrix)

    public override int GetHashCode()

    // Returns the product this x other: the rotation other applied after the rotation this, in the frame of this.
    public Quaternion Multiply(Quaternion other)

    // Norm of the quaternion (1 for a rotation)
    public double Norm { get; }

    // Returns this quaternion with a norm of 1
    public Quaternion Normalize()

    // Scalar part
    public double Qw { get; set; }

    // X component of the vector part
    public double Qx { get; set; }

    // Y component of the vector part
    public double Qy { get; set; }

    // Z component of the vector part
    public double Qz { get; set; }

    // Spherical linear interpolation between two orientations, on the shortest way
    public static Quaternion Slerp(Quaternion start, Quaternion end, double t)

    // Returns the rotation axis and angle: [x, y, z, angle in degrees]. The axis is a unit vector and the angle is between 0 and 180.
    public double[] ToAxisAngle()

    // Returns the 3x3 rotation matrix of this orientation
    public double[,] ToRotationMatrix()

    public override string ToString()
}
```

**Members of Common.XYZWPRPosition**
```csharp
public class XYZWPRPosition : XYZPosition {
    // Default constructor
    public XYZWPRPosition()

    // Constructor with position and rotations
    public XYZWPRPosition(double x, double y, double z, double w, double p, double r)

    public override bool Equals(object obj)

    // Converts a flange position to the position of the tool center point (TCP)
    public XYZWPRPosition FlangeToTcp(XYZWPRPosition tool)

    public override int GetHashCode()

    // Returns the orientation W, P, R as a quaternion
    public Quaternion GetQuaternion()

    // Returns the inverse of this frame
    public XYZWPRPosition Inverse()

    // Returns the composition of this frame with another one: the pose <code class="paramref">other</code>, expressed in this frame, converted to the frame where this position is expressed.
    public XYZWPRPosition Multiply(XYZWPRPosition other)

    // P rotation in degrees (Ry)
    public double P { get; set; }

    // R rotation in degrees (Rz)
    public double R { get; set; }

    // Sets the orientation W, P, R from a quaternion. Angles are between -180 and 180 degrees.
    public void SetQuaternion(Quaternion quaternion)

    // Converts a position of the tool center point (TCP) to the flange position
    public XYZWPRPosition TcpToFlange(XYZWPRPosition tool)

    // Convert position to a homogeneous rotation and translation 4x4 matrix
    public double[,] ToHomogeneousMatrix()

    public override string ToString()

    // Converts this position, expressed in a user frame, to the world frame
    public XYZWPRPosition UserFrameToWorld(XYZWPRPosition userFrame)

    // W rotation in degrees (Rx)
    public double W { get; set; }

    // Converts this position, expressed in the world frame, to a user frame
    public XYZWPRPosition WorldToUserFrame(XYZWPRPosition userFrame)
}
```