Visualizing forces and other vectors

10.4. Visualizing forces and other vectors#

In addition to points and frames (local coordinate systems), the Player can represent forces or other vectors using its vectors property.

We start by loading a gait c3d file.

import kineticstoolkit.lab as ktk
import numpy as np

contents = ktk.read_c3d(
    ktk.doc.download("c3d_sample.c3d"), convert_point_unit=True
)

p = ktk.Player(contents["Points"])

Let’s add a vector at the right shoulder, which could, for example, represent the joint reaction force. For now, we will simply create a dummy sinusoidal vector:

forces = ktk.TimeSeries(time=contents["Points"].time)
forces.data["ShoulderForce"] = 500 * ktk.geometry.create_vector_series(
    y=np.sin(3 * forces.time)
)

Now we create a new Player that shows both points and forces:

p = ktk.Player(contents["Points"], forces)

The force is not shown yet because the Player does not know how to represent the vector “ShoulderForce”. This is configured by the Player’s vectors property, which is a dict where:

  • The key is the name of the force. Here, “ShoulderForce”.

  • The value is a dict with:

    • “Origin”: The name of the point the vector originates from. Here, “r should”, as read from the C3D file.

    • “Scale”: Optional, the scale of the vector in meters per vector unit. It is 1 by default, but here we will set 0.001, which means in this case that the line will be one millimetre per newton.

    • “Color”: Optional, an RGB colour tuple.

To show the shoulder force:

p.vectors["ShoulderForce"] = {"Origin": "r should", "Scale": 0.001}

10.4.1. Force platforms#

The previous section was a dummy example to show how to represent vectors in the Player. In this same file, we also have force platform contents. For visualization, we need to downsample it so that everything is on the same sampling rate:

points = contents["Points"]
force_platforms = contents["ForcePlatforms"].resample(points.time)

We can now visualize the points and force platform data in the Player:

p = ktk.Player(points, force_platforms)

As we see, the Player draws the platforms and the ground reaction forces by default, based on the default value of its interconnections and vector properties

p.interconnections
{'ForcePlatforms': {'Links': [['*_Corner1', '*_Corner2'], ['*_Corner2', '*_Corner3'], ['*_Corner3', '*_Corner4'], ['*_Corner1', '*_Corner4']], 'Color': (0.5, 0.0, 1.0)}}
p.vectors
{'*Force': {'Origin': '*COP', 'Scale': 0.001, 'Color': (1.0, 1.0, 0.0)}}

In these values, wildcards * are used to mean:

  • Any point ending with _Corner1 is connected to a matching point ending with _Corner2;

  • Any point ending with _Corner2 is connected to a matching point ending with _Corner3;

  • etc.

  • Any vector ending with Force is connected to a matching point ending with COP.