Basic algebra

From JPCT
Jump to: navigation, search

Objects and CSs

Basic algebra for users. When I hear about Matrixes and Vectors I know that troubles are near, because vector algebra can cause me headache. What I usualy need is a basic cookbook with most basic user recipes, "How to do it," and that is what I want to offer here.

The most basic space is simple: one origin (point 0, 0, 0), one x-axis (vector 1, 0, 0), y-axis and z-axis. But whenever we place an object in such space, we are not necessarily aware that we also create a subspace - the object space. From this moment, we can place other objects not only in the main space but also in this subspace, using the method addChild, or addParent. Each space is defined by it's coordination system, for short CS:

Object3D cube = Primitives.getCube(1f);
world.addObject(cube);
Object3D sphere = Primitives.getSphere(1f);
world.addObject(sphere);
cube.addChild(sphere);
cube.translate(0f,5f,0f);
sphere.translate(5f,0f,0f);

Darai SubspaceExample.png

The cube in this example is defined in the main CS translated by 5 in the Y direction, but because the sphere is the cubes child, it is defined in the cube's CS, so it is moved by 5 in X direction, but not from the origin of the main CS, but from the origin of the cube's CS.

Similar examples can be found also for rotation and scale and when all those features combine, it can be hard to track them.

Scale

Probably the most simple transformation is scale. This transformation changes size of the object from the selected point called pivot.

Darai ScaleExample.png

In this examle, three different squares (red, green and blue) are created from one original (the filled one) only by setting up different pivot point (the circles).

There are three catches using scale:

1) Each object has its own base size coming from the objects mesh.

Solution: The base mesh size can be found, look at the help of the method Mesh.getBoundingBox()

float[] bbox = object.getMesh().getBoundingBox();

2) The object's pivot is calculated point which means that the basic scale is not well predictable

Solution: The pivot is calculated during the Object3D.build() command. After this line, the pivot can be read and edited

object.build();
SimpleVector pivot = object.getRotationPivot();
object.setRotationPivot(newPivot);

3) The object is also scaled by all the scales of it's parents, so if the size of the mesh is 1 and the object's scale is 1 that does not mean that the size will be 1, because the object can have a parent with a scale not-equal 1.

Solution: Calculate the size from the scale of all object's parents.

	public static SimpleVector getSize(Object3D object){
		float[] bbox = object.getMesh().getBoundingBox();
		float s = object.getScale();
		Object3D[] par;
		par = object.getParents();
		while(par.length>0){
			s = s*par[0].getScale();
			par = par[0].getParents();
		}
		SimpleVector out = new SimpleVector((bbox[1]-bbox[0])*s,(bbox[3]-bbox[2])*s,(bbox[5]-bbox[4])*s); 
		return out;
	}

Rotate

There is no problem with rotation, as long as the object or its parent is translated or scaled, the achieved rotation is always the same. But if you start to mess around with rotation of the parent or setting up the pivot point, the result can be suprising. Whenever some object's ancestor is rotated, it's CS is rotated as well so the final rotation of our object is done by different axis than you may think. Whenever the object's or it's ancestor's pivot is translated, the final orientation will be the same, but the placement of the rotated object may be totally off.

red.addParent(blue);
green.setRotationPivot(new SimpleVector(-5f,0f,0f));
blue.translate(0f,5f,0f);
...
green.translate(5f,0f,0f);
green.rotateZ((float)(Math.PI));
yellow.translate(5f,0f,0f);
yellow.rotateZ((float)(Math.PI));
red.translate(5f,0f,0f);
red.rotateZ((float)(Math.PI));

Darai RotateExample.png

On the example, the yellow, the green and the red cube is translated by the same vector 5,0,0 and rotated by the same angle rotateZ(PI), but:

1) The red cube is in the CS of the blue cube

2) The green cube has translated pivot by vector -5,0,0

All transformations are held in a matrix called "transformation matrix". Clever mathematician can use this matrix to calculate two sub-matrixes the translation matrix and the rotation matrix. To get the rotation matrix for our object, we can use following methods

Matrix m = object.getRotationMatrix();

This gets the object's rotation matrix, but beware; it is in the object space, or in other words in it's parent's CS, not in the global CS.

Matrix m = object. getWorldTransformation();

This is method I still didn't fully tested, but it should get the transformation matrix, you need to get in the original CS, to transform from 0,0,0 with no orientation and scale 1, to the centre of the object with the object's orientation and scale. But I found it's usage tricky and if somebody has a good example, I would be happy to see it.

Translate

Translate something can be in simple cases enough. When we do not rotate or scale ancestors, to translate something means to transform it. And also when we don't need to orient objects, we can translate them quite safely. The nice thing about translation is that even though it is represented by "translation matrix", it can be easily recalculated to translation vector, which is something we can easily imagine.

To get the translation of an object, we can use following methods:

object.getTranslation();

Beware, it is in the object space, so in it's parent's CS.

object.getWorldTransformation().getTranslation();

This should do the trick to get the real translation in the Original CS of the world.

object.getTransformedCenter();

This command gets the object's center's coordinates in the original CS. It can be the same as the getWorldTransformation().getTranslation(), as long as the center is not moved by the setCenter() method.


In the following example I would like to move an object, but the object is not any more in the original CS. In this case, I need to get the new coordinates in the CS, the object is in.

Darai TranslateExample.png

In this picture, we have a red cube, which we want to move into its new position. To do it, we need to have the purple vector to do the translation and what's more, we need to have this vector in the CS the red cube is defined in. That is the CS of the blue cube, which is rotated, better and better. If we have an object in some unknown CS, to get it in the original CS is easy:

object. getWorldTransformation().getTranslation();

But the other way? From the original CS, to the object's CS? That can be tricky. I solved such a problem with following method. This method takes a vector toRecalc (the black one in the picture) and calculates the vector in the CS, in which the object is defined in (the purple vector).

	public static void recalcVector(SimpleVector toRecalc, Object3D forObject){
		toRecalc.sub(forObject.getWorldTransformation().getTranslation());
		Object3D[] p = currObj.getParents();			
		while(p.length>0){
			toRecalc.matMul(p[0].getRotationMatrix().invert());
			p = p[0].getParents();
		}
	}

Problem: In this example I don't count with scale, I expect that the scale of parent can influence the translation of child, so if the scale should be count in, I would have to scalarMul() the vector toRecalc by 1/totalScale of all the parents of the forObject. I first used the method:

toRecalc.sub(forObject.getWorldTransformation().getTranslation());

This way I have the vector I want. The only problem is that the vector needs to be rotated, because it is still in the original CS. That is why I used the second method:

toRecalc.matMul(p[0].getRotationMatrix().invert());

This way I rotated the vector using inverted Rotation matrix of the object p[0] (the parent).