Path builders describe a trajectory as the instructions of a TP program. Each method adds a motion and returns the builder, so the calls can be chained. `Build()` creates the trajectory.

![The same start and target with a joint (J), a linear (L) and a circular (C) motion, seen from above.](/fanuc/documentation/diagrams/motion-joint-linear-circular.svg)

## Joint motions

A joint motion moves all axes together on a straight line in joint space: they start and stop at the same time.

**C# : MotionMovesJoint**
```csharp
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Motion;
using UnderAutomation.Fanuc.StreamMotion.Data;
using UnderAutomation.Robotics.Geometry;
using UnderAutomation.Robotics.Motion;

public class MotionMovesJoint
{
    static void Main()
    {
        // Limits of the robot (example values). Read them with robot.StreamMotion.ReadLimits().ReferenceLimits
        var jointLimits = new JointLimits(
            new double[] { 120, 120, 180, 180, 180, 180 },
            new double[] { 300, 300, 450, 675, 675, 675 },
            new double[] { 1125, 1125, 1687, 2530, 1265, 2530 });
        var cartesianLimits = new CartesianLimits(500, 2000, 10000, 90, 360, 1800);

        /**/
        var planner = new MotionPlanner(jointLimits, null);
        var start = new JointValues(0, 0, 0, 0, -90, 0);
        var p1 = new JointValues(40, 0, 0, 0, -90, 0);
        var p2 = new JointValues(40, 30, -20, 0, -60, 0);

        Trajectory trajectory = planner.CreateJointPath(start)
            .MoveJoint(p1, 100, FanucMotion.Cnt(50))            // J P[1] 100% CNT50
            .MoveJoint(p2, 30, FanucMotion.Fine(), 50)          // J P[2] 30% FINE ACC50
            .SetIO(FanucMotion.Signal(IOType.DO, 1), true)      // DO[1]=ON when the robot is at P[2]
            .Wait(0.5)                                          // WAIT 0.50(sec)
            .MoveJointTime(start, 2.0, FanucMotion.Fine())      // back in 2 s
            .Build();
        /**/
    }
}
```

**Python : MotionMovesJoint**
```python
from underautomation.fanuc.motion.fanuc_motion import FanucMotion
from underautomation.fanuc.stream_motion.data.io_type import IOType
from underautomation.robotics.geometry.joint_values import JointValues
from underautomation.robotics.motion.joint_limits import JointLimits
from underautomation.robotics.motion.cartesian_limits import CartesianLimits
from underautomation.robotics.motion.motion_planner import MotionPlanner

# Limits of the robot (example values). Read them with robot.stream_motion.read_limits().reference_limits
joint_limits = JointLimits(
    [120, 120, 180, 180, 180, 180],
    [300, 300, 450, 675, 675, 675],
    [1125, 1125, 1687, 2530, 1265, 2530])
cartesian_limits = CartesianLimits(500, 2000, 10000, 90, 360, 1800)

##
planner = MotionPlanner(joint_limits, None)
start = JointValues([0, 0, 0, 0, -90, 0])
p1 = JointValues([40, 0, 0, 0, -90, 0])
p2 = JointValues([40, 30, -20, 0, -60, 0])

trajectory = (planner.create_joint_path(start)
    .move_joint(p1, 100, FanucMotion.cnt(50))            # J P[1] 100% CNT50
    .move_joint(p2, 30, FanucMotion.fine(), 50)          # J P[2] 30% FINE ACC50
    .set_io(FanucMotion.signal(IOType.DO, 1), True)      # DO[1]=ON when the robot is at P[2]
    .wait(0.5)                                           # WAIT 0.50(sec)
    .move_joint_time(start, 2.0, FanucMotion.fine())     # back in 2 s
    .build())
##
```

![Joint positions of this trajectory. J4 and J6 do not move.](/fanuc/documentation/diagrams/motion-moves-joint.svg)

| Method | TP equivalent |
| --- | --- |
| `MoveJoint(target, speedPercent, termination, accelerationPercent)` | `J P[1] 50% CNT100 ACC80` |
| `MoveJointTime(target, duration, termination)` | Joint motion in a given time (longer if the limits need it) |
| `Wait(duration)` | `WAIT 0.50(sec)` |
| `SetIO(FanucMotion.Signal(IOType.DO, 1), true)` | `DO[1]=ON` after the previous motion |
| `MoveJointSpline(points, speedPercent, termination)` | Smooth motion through several positions, see [Splines & shapes](/fanuc/documentation/motion-splines-shapes) |

## Cartesian motions

Linear and circular motions move the tool center point at the given speed in mm/s. The orientation turns progressively from the start orientation to the target orientation.

**C# : MotionMovesCartesian**
```csharp
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Motion;
using UnderAutomation.Robotics.Geometry;
using UnderAutomation.Robotics.Motion;

public class MotionMovesCartesian
{
    static void Main()
    {
        // Limits of the robot (example values). Read them with robot.StreamMotion.ReadLimits().ReferenceLimits
        var jointLimits = new JointLimits(
            new double[] { 120, 120, 180, 180, 180, 180 },
            new double[] { 300, 300, 450, 675, 675, 675 },
            new double[] { 1125, 1125, 1687, 2530, 1265, 2530 });
        var cartesianLimits = new CartesianLimits(500, 2000, 10000, 90, 360, 1800);

        /**/
        var planner = new MotionPlanner(jointLimits, cartesianLimits);

        // X, Y, Z, W, P, R: the W, P, R angles of FANUC use the fixed XYZ convention
        Func<double, double, double, double, double, double, CartesianPose> wpr =
            (x, y, z, w, p, r) => CartesianPose.FromEuler(x, y, z, w, p, r, EulerConvention.FixedXYZ);
        var start = wpr(500, 0, 300, 180, 0, 0);

        Trajectory trajectory = planner.CreateCartesianPath(start)
            .MoveLinear(wpr(600, 0, 300, 180, 0, 0), 200, FanucMotion.Cr(10))       // L 200mm/sec CR10
            .MoveLinear(wpr(600, 100, 300, 180, 0, 0), 200, FanucMotion.Cnt(100))   // L 200mm/sec CNT100
            .MoveCircular(wpr(550, 150, 300, 180, 0, 0),                            // C via point
                          wpr(500, 100, 300, 180, 0, 30), 150, FanucMotion.Fine())  // target, 150mm/sec FINE
            .MoveLinearTime(start, 1.5, FanucMotion.Fine())                         // back in 1.5 s
            .Build();

        // FANUC positions of the trajectory (W, P, R stay continuous)
        ExtendedCartesianPosition[] samples = FanucMotion.SampleCartesian(trajectory, 0.008);
        /**/
    }
}
```

**Python : MotionMovesCartesian**
```python
from underautomation.fanuc.motion.fanuc_motion import FanucMotion
from underautomation.robotics.geometry.cartesian_pose import CartesianPose
from underautomation.robotics.geometry.euler_convention import EulerConvention
from underautomation.robotics.motion.joint_limits import JointLimits
from underautomation.robotics.motion.cartesian_limits import CartesianLimits
from underautomation.robotics.motion.motion_planner import MotionPlanner

# Limits of the robot (example values). Read them with robot.stream_motion.read_limits().reference_limits
joint_limits = JointLimits(
    [120, 120, 180, 180, 180, 180],
    [300, 300, 450, 675, 675, 675],
    [1125, 1125, 1687, 2530, 1265, 2530])
cartesian_limits = CartesianLimits(500, 2000, 10000, 90, 360, 1800)

##
planner = MotionPlanner(joint_limits, cartesian_limits)

# X, Y, Z, W, P, R: the W, P, R angles of FANUC use the fixed XYZ convention
def wpr(x, y, z, w, p, r):
    return CartesianPose.from_euler(x, y, z, w, p, r, EulerConvention.FixedXYZ)

start = wpr(500, 0, 300, 180, 0, 0)

trajectory = (planner.create_cartesian_path(start)
    .move_linear(wpr(600, 0, 300, 180, 0, 0), 200, FanucMotion.cr(10))       # L 200mm/sec CR10
    .move_linear(wpr(600, 100, 300, 180, 0, 0), 200, FanucMotion.cnt(100))   # L 200mm/sec CNT100
    .move_circular(wpr(550, 150, 300, 180, 0, 0),                            # C via point
                   wpr(500, 100, 300, 180, 0, 30), 150, FanucMotion.fine())  # target, 150mm/sec FINE
    .move_linear_time(start, 1.5, FanucMotion.fine())                        # back in 1.5 s
    .build())

# FANUC positions of the trajectory (W, P, R stay continuous)
samples = FanucMotion.sample_cartesian(trajectory, 0.008)
##
```

![Path of the tool seen from above, and its speed. Z, W and P do not change.](/fanuc/documentation/diagrams/motion-moves-cartesian.svg)

| Method | TP equivalent |
| --- | --- |
| `MoveLinear(target, speed, termination, accelerationPercent)` | `L P[1] 200mm/sec CR10` |
| `MoveCircular(via, target, speed, termination, accelerationPercent)` | `C P[1] P[2] 150mm/sec FINE` |
| `MoveLinearTime(target, duration, termination)` | Linear motion in a given time |

The speed is reduced when the change of orientation, the curvature or the extended axes need it.

## Terminations

| Termination | Behavior |
| --- | --- |
| `FanucMotion.Fine()` | The robot stops at the target |
| `FanucMotion.Cnt(0..100)` | The next motion starts during the deceleration of this one. CNT100 gives the smoothest motion. Joint and Cartesian |
| `FanucMotion.Cr(distance)` | Corner region: the corner is replaced by a smooth curve that starts at this distance (mm) from the target, whatever the speed. Cartesian only, between L and C motions and splines |

These methods return a `Termination` of the planner: `Termination.Stop()`, `Termination.Overlap(percent)` and `Termination.Corner(distance)` give the same result.

![FINE, CNT and CR between two linear motions at 200 mm per second. Top: path near the corner. Bottom: speed.](/fanuc/documentation/diagrams/motion-terminations.svg)

With CNT, the size of the rounded corner depends on the speed, as on a FANUC controller:

- Joint motions: the next motion starts during the deceleration of this one. The overlap is reduced automatically when the combination of both motions would exceed the limits.
- Linear and circular motions: the corner is replaced by a smooth curve that starts where the robot would start to decelerate (CNT100), or closer to the target (CNT50: half of this distance).

With CR, the corner geometry is fixed: the curve passes at about 0.12 x distance from the corner for a change of direction of 45 degrees, 0.25 x distance for 90 degrees and 0.4 x distance for 135 degrees. The distance is limited to half of the length of each motion.

In a CNT or CR curve, the speed is constant, and reduced when the curvature needs it: a sharp corner with a small distance is followed slowly. For a fast motion, use a larger CNT or CR distance.

## Tool and user frames

By default, Cartesian positions are flange positions in the world frame. Set `ToolFrame` and `UserFrame` to give the targets as positions of a tool in a user frame, as with UTOOL and UFRAME on the teach pendant:

**C# : MotionMovesFrames**
```csharp
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Motion;
using UnderAutomation.Robotics.Geometry;
using UnderAutomation.Robotics.Motion;

public class MotionMovesFrames
{
    static void Main()
    {
        // Limits of the robot (example values). Read them with robot.StreamMotion.ReadLimits().ReferenceLimits
        var jointLimits = new JointLimits(
            new double[] { 120, 120, 180, 180, 180, 180 },
            new double[] { 300, 300, 450, 675, 675, 675 },
            new double[] { 1125, 1125, 1687, 2530, 1265, 2530 });
        var cartesianLimits = new CartesianLimits(500, 2000, 10000, 90, 360, 1800);

        /**/
        var planner = new MotionPlanner(jointLimits, cartesianLimits);

        // Targets are positions of this tool, in this user frame
        planner.ToolFrame = FanucMotion.ToCartesianPose(new XYZWPRPosition(0, 0, 150, 0, 0, 0));      // UTOOL: 150 mm along Z of the flange
        planner.UserFrame = FanucMotion.ToCartesianPose(new XYZWPRPosition(800, -200, 0, 0, 0, 90));  // UFRAME, relative to the world frame

        // The start is the position of the robot (flange in the world frame)
        var builder = planner.CreateCartesianPath(FanucMotion.ToCartesianPose(new XYZWPRPosition(700, 0, 400, 180, 0, 0)));

        // EndPosition gives the same position as a tool position in the user frame
        CartesianPose tcp = builder.EndPosition;
        var target = new CartesianPose(tcp.X + 50, tcp.Y, tcp.Z, tcp.Orientation);

        // The trajectory gives flange positions in the world frame, ready to send to the robot
        Trajectory trajectory = builder.MoveLinear(target, 100, FanucMotion.Fine()).Build();
        /**/
    }
}
```

**Python : MotionMovesFrames**
```python
from underautomation.fanuc.common.xyzwpr_position import XYZWPRPosition
from underautomation.fanuc.motion.fanuc_motion import FanucMotion
from underautomation.robotics.geometry.cartesian_pose import CartesianPose
from underautomation.robotics.motion.joint_limits import JointLimits
from underautomation.robotics.motion.cartesian_limits import CartesianLimits
from underautomation.robotics.motion.motion_planner import MotionPlanner

# Limits of the robot (example values). Read them with robot.stream_motion.read_limits().reference_limits
joint_limits = JointLimits(
    [120, 120, 180, 180, 180, 180],
    [300, 300, 450, 675, 675, 675],
    [1125, 1125, 1687, 2530, 1265, 2530])
cartesian_limits = CartesianLimits(500, 2000, 10000, 90, 360, 1800)

##
planner = MotionPlanner(joint_limits, cartesian_limits)

# Targets are positions of this tool, in this user frame
planner.tool_frame = FanucMotion.to_cartesian_pose(XYZWPRPosition(0, 0, 150, 0, 0, 0))      # UTOOL: 150 mm along Z of the flange
planner.user_frame = FanucMotion.to_cartesian_pose(XYZWPRPosition(800, -200, 0, 0, 0, 90))  # UFRAME, relative to the world frame

# The start is the position of the robot (flange in the world frame)
builder = planner.create_cartesian_path(FanucMotion.to_cartesian_pose(XYZWPRPosition(700, 0, 400, 180, 0, 0)))

# end_position gives the same position as a tool position in the user frame
tcp = builder.end_position
target = CartesianPose(tcp.x + 50, tcp.y, tcp.z, tcp.orientation)

# The trajectory gives flange positions in the world frame, ready to send to the robot
trajectory = builder.move_linear(target, 100, FanucMotion.fine()).build()
##
```

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

- The start of `CreateCartesianPath()` is the position of the robot: flange in the world frame, for example `FanucMotion.ToCartesianPose(StreamMotion.QueueEndCartesianPosition)`.
- `EndPosition` gives the end of the motions added so far, in the tool and user frames, as a `CartesianPose`.
- The trajectory gives flange positions in the world frame, ready to send to the robot. The Cartesian limits apply to the tool center point.

## API reference