Implementing type-safe position, velocity and acceleration types

As discussed in this post there are some potential advantages in representing physical quantities like position, velocity, and similar in a type-safe way.

Instead of using simple floating point types, we can write our own wrapper types that have semantic meanings like ‘length’, ‘speed’, or ‘time’ and only allow physically correct operations between them.

That is exactly what we will look into here. We will implement the basic types for such a system, and show what operations between them are relevant and well defined.

Physical units of interest

Of course there is a large number of possible physical units that we could try and represent. If we were to make a type for every single such unit, we would have a lot of work to do.

More importantly, there are an infinite number of unit combinations. For instance, we could consider not only units like length, but also length squared, or cubed, or to any other power.

It is of course entirely possible to write a system supporting such arbitrary units.

However, since this blog is about game development, we will limit ourself to the kind of units and their combinations that are at the core of almost any game.

This means will limit ourselves entirely to the types that are related to the movement of objects. Of course our approach can be used for other areas as well, but speaking of locations and velocities will be a great and familiar example – and the most useful for actual games.

In the previous post we already mentioned the example types of Vector, Velocity, Acceleration and Time, corresponding to the units of distance, distance/time, distance/(time*time), and time itself respectively.

The relationship between these types is fairy easy, so we will use them as our starting point.

Note that we are not actually talking about ‘meters’ or ‘seconds’ for the units of our types. I want to refrain from such qualifications, since many games may not use them as their basic unit of measurements. One unit of distance might as well refer to one kilometer, mile, or an arbitrary length like the width of a tile. Similar, games might measure time in months, years, or milliseconds. By talking about ‘distance units’ and ‘time units’ instead of these concrete examples, we remain much more flexible and make our code more reusable without causing confusion.

One dimension

To begin as easy as possible, let us start with only a single dimension of space.

In this one dimension, an objects location can be represented by a single signed number. The same goes for it’s velocity and acceleration. Unsurprisingly, time is exactly one single dimension as well, and can also be represented as such.

Since the names of Vector and Velocity imply directions, I am going to instead use the names of Unit and Speed to represent distance/length and velocity in our single dimension.

The choice of these names is mostly esoteric, but I think they result in neat and easily understood code as well.

Note: Physically speaking, speed would be undirected in the sense that it cannot be negative. I still think that it is a better choice of name however, which I hope will become clear below.

Timespans vs. timestamps

To be able to use Unit, Speed and Acceleration in the same equation, we need types to represent the concept of time.

What I noticed when implementing this was that there is a distinct semantic difference between the concepts of ‘timespan’ (the length of an interval of time) and ‘timestamp’ (a fixed point in time).

Both of these concepts have the unit ‘time’ (for example measured in seconds), but we still would not want to use them interchangeably.

For example, it makes no semantic sense to add two timestamps: 9:00 + 10:00 = ??. While this is mathematically allowed, it makes no sense in context. Adding timespans however makes perfect sense: 1h + 2h = 3h.

Since the whole reason we are writing any of these types is to be able to detect their misuse and bugs caused by it at compile time, I decided to add two distinct types for the two semantic meanings: Timespan and Instant.

Position vs. distance/length

You may notice that our Unit type could be split in a similar fashion as Timespan and Instant. There is a clear semantic difference between the concept of position, and the difference between two positions, even though both are measured in the same unit of length (for example meters).

However, I think that this would be an unnecessary over complication.

In fact, I tried splitting the type like that, but quickly discovered that values representing one dimensional absolute positions are hardly ever used, except as components of higher dimensional vectors, and a few special cases.

Therefore I made the decision not to make this distinction here. Of course, if you think that the distinction is important, you can make the appropriate changes. Also feel free to let me know if you think you have a good argument I may have overlooked.

Operations within and between types

Most of the types are very similar when it comes to their operations that do not interact with the other ones.

For example, except for Instant (because of its absolute nature), they all are proper one dimensional vector spaces. That means that for example two Units can be added, subtracted, and that any Unit value can be multiplied by a floating point scalar (or divided by an inverse scalar).

In addition, we could divide one Unit value by another, resulting in a scalar representing the ratio of the two values.

The same operations work just as well for Speed, Acceleration and Timespan.

Instant is more limited, since its definition is a stricter one. In fact, we can hardly do anything with it that does not involve any of the other types.

What we can do however is subtract two Instants to receive a Timespan representing the interval or difference between the two. And of course we can add a Timespan to an Instant to get a new Instant to progress time.

Further, we can integrate Speed by multiplying it with a Timespan, returning a Unit value. Similarly, Acceleration integrates to Speed.

On the other side we can also differentiate Unit and Speed values by dividing by a Timespan to get Speed and Acceleration values respectively.

In total that makes for the following list of possible operations:

// vector fields (with unit as example)
// the same also for speed, acceleration and timespan
unit + unit = unit
unit - unit = unit
unit * scalar = unit
unit / scalar = unit
unit / unit = scalar

// instant
instant - instant = timespan
instant + timespan = instant

// integrations
speed * time = unit
acceleration * time = speed

// differentiation
unit / time = speed
speed / time = acceleration

These are all the operations we need to be able to do arithmetic with these types in a completely type-safe fashion.

Note that I have not given any actual code. The implementation of any of these operators are simple one-liners, and the types itself are nothing more than a wrapper around a floating point value.

If you are interested in my implementation however, feel free to check the full code on GitHub.

Conclusion

I hope this small introduction into the implementation of type-safe physical unit types has been interesting to you.

If so, make sure to share the post on your favourite social media.

Make sure to check back next week when I will talk about implementing similar types for more than one-dimensional spaces.

And of course feel free to leave a comment if you have any questions or other feedback.

Enjoy the pixels!

Leave a Reply

4 comments

  1. xooxdoo says:

    Hi, could you please do a tutorial on how to implement INativeWindow of OpenTK :D thanks forward.

    • Paul Scharf says:

      I got some plans for a few more posts on OpenTK.
      Can’t make a promise about when I’ll be able to squeeze them out, but I’ll try to include this.

      Thank you for the suggestion! :)

  2. Varon says:

    You might be interested in checking out F#’s type-safe units of measure. They offer a language-level alternative to this. Another alternative would be, instead of duplicating the code for each unit of measure you want, would be to utilize phantom types to encode the type information in a generic way.

    • Paul Scharf says:

      Someone else only recently pointed out the units of measure feature in F#, and it indeed looks pretty cool.

      I don’t know anything about phantom types, but will look into it!
      If you mean what I think you might about using generics, that is indeed an interesting idea. I will absolutely take a stab at that when I find some time.

      Thank you for the great ideas!