What is Behaviour?

Behaviour is a mechanics simulator. It's aim is to be able to create simulations which will behave as similar to the real universe as possible. The focus lies in simulating the general behaviour, and not so heavily in precision.

What can Behaviour be used for?

Behaviour can be used for a lot of things. These are some uses which I can come up with:

How does it work?

Behaviour uses a numerical stepping method (Euler's stepping method) to simulate processes. What this means is that to simulate something, Behaviour needs the state of the "universe" for a certain timepoint, and a timeslice. With these parameters, Behaviour will then step forward, one timeslice at a time, and thus simulate the process.

Behaviour uses a mechanical model developed by me, which I think is novel. The main idea of the model is that it's possible to describe the behaviour of many small primitives using fewer larger primitives. It's quite hard to simulate the paths of all the atoms (or molecules) of a group of objects, since there are so many, but if we reduce the number of atoms and compensate for the fact that they must be larger, we can still get roughly the same behaviour of the group.

Objects

The "atoms" are being regarded as point-masses with some additional properties used when dealing with collisions. Worth noting is that even if they have a radius, they cannot rotate. From here on, these "atoms" will be referred to as "objects".

Object Attributes

Here are the attributes of an object:

* = These are also attributes of a "connection" and will be explained later

There are also a number of flags which are not so important for understanding the model, but which will also be explained later.

Connections

To be able to create more interesting configurations than just single objects floating around, we introduce the "connection". For most practical purposes, a connection behaves like a spring connecting two objects, ie. according to Hooke's Law with my own addition of some form of internal damping:

F = k*(l-e) + v*d (Where k is the spring constant, l the nominal length of the spring, e the elongation, v the elongation velocity and d the damping constant)

While it is possible to use connections as simple springs, they are more useful acting as bonds between objects. These bonds are similar to atomic bonds. It has been shown that atomic bonds behave according to Hooke's law for small elongations, so this is a valid approach. What this means is that we can create an arbitrary shape, just arranging and bonding objects in different configurations. Depending on how the bonds are arranged, and how the parameters of the bonds are chosen, we can also get different behaviour for the same shapes. For example, we can form a cube by making an array of objects. If we don't create any bonds, the cube will fall apart, if we create many stiff bonds, it will appear to be a hard cube, if we create many soft bonds, it will appear to be a rubber or jelly cube. We can also either make the cube hollow, or solid, which will affect how it behaves.

Connection Attributes

Here are the attributes of a connection:

Universal Laws

There are also some universal laws governing any simulation. These are:

Collisions

Up until now, the model has been reasonably anchored in classical mechanics. Collision handling though, is not to my knowledge treated very well by classical mechanics, at least not well enough to be suited for simulation. In classical mechanics, it's possible to calculate what a system looks before and after a fully elastic collision, but it's not possible to know what it looks like during the collision. Also, as soon as the collision is not fully elastic, there will be problems.

In my model, when the radii of two objects intersect, a collision occurs, and it is represented by forming a connection between the objects. The parameters of the connection are inherited from the objects, the mean of the objects' collision parameters is taken, and given to the connection (it is these attributes that are in the attribute list for the object above). The length of the new connection will be the sum of the radii. Although I haven't seen proof of it, this must be what happens when real objects collide, ie. it is the atomic forces which control the collision.

With this system it's possible to simulate any type of collision. A fully elastic collision is simulated by setting the damping constant and the tolerance to 0, an inelastic collision will be the opposite. The tolerance will act as some form of stickiness, and the damping will represent the loss of energy.

Friction

Friction handling is still a little in flux. Right now friction is handled by the connections, and is represented by sliding friction:

F = u*N (Where N is the normal force on the object, and u is the friction constant. The friction force is always oriented opposite the direction of velocity.)

When two objects collide, the mean of the friction constants is taken and assigned to the new connection.

One problem with this approach is that this representation of friction is macroscopic, while the rest of the model tries to be microscopic. What I mean by this is that this representation of friction is an approximation of many different processes acting together, while the rest of the model tries to isolate processes as much as possible. There might be deformation involved in friction, even destruction of the outer layer of material of the objects, there might be heating etc. This only works if we can define a surface where all this happens, but in this model, every object has a surface all the way around. For friction between two separate objects, this works well, because that configuration mirrors the way friction occurs. However, if we connect some objects and expect them to behave like a single object, we will get friction on the inside of this single object, between every object that is a part of it and touches another object that is part of this aggregate.

There is a partial solution to this problem, and that is already inherent in the model, but it is not a complete solution. I have defined collision to only be able to occur between objects which are not already connected. This is logical, since otherwise a "collision bond" would be added every timeslice when two objects intersect. What this means for friction is that we can eliminate the friction constant for connections which we consider "bonds", and not otherwise. Still, if we deform the aggregate of objects enough, the objects inside might start touching not only their connected neighbors, but also other internal objects. Of course we could make dummy connections between every internal object, but this doesn't solve the problem completely either. If we break the aggregate into pieces, some of the exposed surfaces will not be frictional, since they are already connected with dummy connections.

For some situations, this representation of friction works, and for others not. It is possible to simulate "real" friction using what we have, ie. simply by making surfaces slightly irregular, and making sure they are damped. This consumes quite a lot of resources though, so I will keep the flawed friction representation until it can be replaced with something better. One example of abominations that can occur with it is if we have an array of objects forming a cube. If we compress this cube enough and rotate it, friction will work against the rotation, causing the cube to lose kinetic energy without any external forces/processes acting on it.

How is the program used?

The parts of the program

Behaviour can be said to consist of three parts:

The parser parses files written in a description language (which will be explained a little later) and thus builds up a state for the simulator, and some rules for the processor. The simulator simulates, as described above, and the processor processes the information contained in the simulator, ie. it handles output. The processor part will be known as a "camera" from now on, though it's more than a camera. For each simulation there can be only one parser, but several cameras.

The camera

To be able to have any practical use of the model, we must devise some way of observing the model, and choosing which parts we want to observe and which parts to ignore. The camera has access to most of the state of the simulator, and is notified and given a chance to do something every time the state changes (every timeslice). The camera has an assignable samplerate value, which controls how many timeslices should pass between every camera calculation. A samplerate of 1 means the camera should calculate every timeslice, a value of 2 every second, a value of 3 every third aso. When the camera decides to calculate, it has some tools available which make the calculation phase useful.

The description language

I've created a description language for Behaviour which structurally is similar to the language used by the Persistence of Vision raytracer (POVRay). It is based on blocks, where a block is a pair of {} brackets preceded by some keywords. An example:

object
{
	radius	{ 1 }
	mass	{ 10 }
}
Here we can see that mass { 10 } is itself a block, so the block concept is nested. The order of blocks is not fixed, but is important. If I have two mass blocks after one another inside an object block, the second block will take precedence, and overwrite what the first block assigned. Note also that whitespace is whitespace, so I could just as well have written the example like this:
object { radius	{ 1 } mass { 10 } }

Types and values

As the description language will want to handle values of different kinds, it's useful to define some basic types. Note that every block doesn't return a type, so some blocks can only be used in pre-defined places. The basic types are:

There are more types available, but these three are the only ones which can be used in expressions, except when using the point operator (.), which can extract values from some other types. This is documented in the operator tables below. Here are the additional types:

Every value (of the three basic types) can either be constant or variable. Please note that these concepts may not be the same as in programming languages. A constant is assigned an expression when declared, and this expression is evaluated immediately. A variable on the other hand, re-evaluates the expression (called function) that is assigned to it, every time the camera is updated.

A constant value is declared like this:

constant [type]
{
	name		{"[name]"}
	expression	{ [expression returning type] }
}

A variable value is declared like this:

variable [type]
{
	name		{"[name]"}
	function	{ [expression returning type] }
}

Note that variable values can only be declared and used inside cameras, so I will not differentiate between constant or variable values from now on. Anywhere where a variable value is used, a constant value can be used. The opposite is never true.

These declarations don't return anything, so they can't be used in expressions. To access a declared value from an expression, one does this:

constant [type] { "[name]" }

or

variable [type] { "[name]" }

or with every type except these basic three simply

[type] { "[name]" }

Additionally, there exists a special way of accessing the object or connection we are in the process of declaring: (note that this only applies for objects and connections)

[type] {this}

Expressions

An expression is any number of values combined with some predefined operators. The operators returning real are:

The operators returning vector are: The operators returning string are: The operators returning object are:

Declarations

A declaration corresponds to what an assignment does in other languages. However, there is a difference. Declaring two values with the same name does not overwrite the first one, but subsequent attempts to retrieve the value will only find one of them. A declaration which expects a name and does not get one will be named the empty string "". If there is no need to retrieve the value, one can safely ignore assigning a name. The namespace for different types is not shared, with the exception of constants/variables, where the namespace is shared for all types. Declarations can also be unnamed, ie. declaring the mass inside an object assigns the new mass value to the object, which is already named, so there is no need to name the mass of the object.

Most declarations have a template equivalent, where the only difference is that the template declaration is preceded by the word template, and that templates can be retrieved inside the normal declarations with the block template {[string]}. A template fills in all the member values with that of the template declaration. Templates and normal declaration do not share the same namespace.

Simulator declarations

We can imagine that we are already inside a block, the simulator block. Inside this block we will want to declare more blocks, both to declare member values of the simulator (timeslice size for example), and blocks to build up the universe. Here are the valid blocks inside the simulator block:

Object declaration

Valid blocks in an object declaration:

Connection declaration

Valid blocks in a connection declaration:

Camera declaration

Valid blocks in a camera declaration:

Triangle declaration

Valid blocks in a triangle declaration:

Vertex declaration

Valid blocks in a vertex declaration:

Sphere declaration

Valid blocks in a sphere declaration:

Line declaration

Valid blocks in a line declaration:

Export declaration

Valid blocks in an export declaration: