Workflow

The scripting API provides the necessary tools for plotting and analyzing data collected during the simulation. Please see the following API Documentation pages for further details:

Data Collection

Start by creating a script component and add it to an entity. This entity can either be the one you would like to collect data from, via this.entity, or you can collect data from other entities. To record data from a different entity, you can either use a public handle:

export class Graph extends Component {
    public ball: Handle<Entity>;

    public constructor(entity: Entity) {
        super(entity);
        this.ball = this.handle(Entity);
    }
}

Or you can query the world as shown below:

export class Graph extends Component {
    #ball: Entity|null;

    public constructor(entity: Entity) {
        super(entity);
        this.#ball = null;
    }

    public override initialize(): void {
        this.#ball = this.world.find("Ball");
    }
}

With a public handle, you can search for and select the desired entity for your custom script component through the inspector. If you want to query the world, note that if there are multiple entities named “Ball”, then find() will return only the first entity that was found.

Example

In this example, we demonstrate how to plot the height of a ball that bounces over time. Both the ball and the floor are assigned a custom physics material that has a non-zero coefficient of restitution.

Bounce Example

The source code for this script component is provided below:

import { type Entity, Component, LineSeries, Analysis } from "prototwin";

export class Graph extends Component {
    #lineSeries: LineSeries;

    constructor(entity: Entity) {
        super(entity);
        this.#lineSeries = new LineSeries();
    }

    public override update(dt: number): void {
        this.#lineSeries.add(this.world.time, this.entity.position.y); // Add a data point (x, y) to the series.
    }

    public override initialize(): void {
        const linePlot = Analysis.plots.add("graph", "Bounce"); // Create a new plot with the unique name "graph".
        linePlot!.add(this.#lineSeries); // Add a series to the plot.
    }
}

This component records the y-axis position of the entity to which it is attached every timestep. When adding a plot, using Analysis.plots.add(), the first argument specifies the unique name for the plot. The second (optional) argument specifies the title of the plot. The function will return the created plot, or null if the provided name was not unique.

You can add multiple series to a plot (see the line series example). In this example, we just add a single line series, connecting each data point using a straight line. The component’s update() function adds a new data point every timestep.

Analysis Pane

You can view all the plots by toggling the analysis pane through the toolbar. The analysis pane is resizable by dragging the left edge. Each plot may be saved to an image. You can also export the data from each plot to a CSV file.