Understanding Matrix in SVG - javascript

I need help in deep understanding of matrix in SVG. I already know about matrix, I want to rotate and scale without using scale or rotate word. I want to use transform='matrix(a,b,c,d,e,f)'. I know 'a/d' value determine the scale, 'e/f' determines the position. tan(b),tan(c) determines the skew. cos(a),sin(b),-sin(c),cos(d) determines the angle.But I want to know how this work, I need thoroughly help in understanding matrix in SVG.

Matrix operations are composed of individual, "local" transformations (i.e. translate, rotate, scale, skew) by matrix concatenation (i.e. multiplication).
For example, if you want to rotate an object by r degrees around a point (x, y), you would translate to (x, y), rotate r degrees, then translate back to the original position (-x, -y).
By what is often referred to as "chaining" (as described above) each successive "local" transformation is combined to produce a result. Therefore, at any location in a chain of transformations, the "local" transformation space (at that location) is composed of all operations that came before.
What this implies is that when transforming some parameter of an SVG element (i.e. translate) the transform is applied to it's current transformation space. So, for example if the element is already rotated 30 degrees, then a translation of (8, 5) would not go 8 to the right and 5 down, but it would go the rotation of (8, 5) by 30 degrees - relative to the current position.
So this is a bit of a gotcha.
One way to help deal with this complication is to decompose transformation matrices into their individual, total transformations (i.e. total translation, total rotation/skew, total scale), but decomposition says nothing about what individual basic transformations went into the combined totals, nor of the order in which they occurred. This is a problem because 2D transformations are not commutative, e.g. translate(x, y)->rotate(r) is not the same as rotate(r)->translate(x, y).
The best way that I've found is to only compose transformations in a certain order and keep track of the totals in that order, then when a new transformation is introduced, used the totals that have been tracked, update the one that is being modified and recompose the entire transformation.
Like so: (pseudo-code)
// EDIT: initialize components (new SVGMatrix returns the identity matrix)
var transX=0, transY=0, rot=0, scaX=0, scaY=0, skwX=0, skwY=0, matrix = new SVGmatrix();
// example rotate
function rotate(svgEl, angle){
rot = rot + angle;
updateTransform();
applyTransform(svgEl);
};
function updateTransform(){
// the order that I've found most convenient
// (others may do it differently)
matrix.translate(transX, transY);
matrix.rotate(rot);
matrix.scale(scaX, scaY);
matrix.skewX(skwX);
matrix.skewY(skwY);
};
function applyTransform(el){
el.transform = matrix;
};
To be clear, this is not suggesting that matrices are not a good way of representing transformations, nor is it suggesting a better way - far from it.
Transformation matrices are a powerful tool, and when used appropriately, they are very effective at handling complex animations, but they are not trivial to use in all cases.
This may be a bit advanced, but for more information about animations using matrix transformations, this short code example provides a wealth of information and references to start from.
http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition
Update:
Just a note about the decomposed skew factor proposed at the above link.
Only a single skew factor ( in x ) is computed because skewing in both x and y is equivalent to a skew in x and a combined ( offset ) rotation.
Combining x skew and y skew ( with or without a rotation or translation, as in my above preferred composition order ) will result in a different x skew, rotation ( e.g. non-zero rotation if none was originally composed ), and translation ( e.g. an offset by some amount relative to the decomposed rotation in lieu of the original y skew ), but no recoverable y skew - using the linked decomposition method.
This is a limitation of composed affine matrices. So producing a final result matrix should generally be considered a one-way computation.

Related

THREE.js: Can you force a different order of operations for three.js?

See this question: Threejs Transform Matrix ordering
I'm drawing the orbits of the planets and having problems with changing inertial reference frames (I chose to align the three.js world coordinates with the viewed planet's inertial reference frame). But I need to force three.js to apply operations in the order shown below so the orbit is properly displayed (presently the orbit stays at world (0,0,0) and spins crazily).
The normal (physics) way to do this is to do the following in order:
zero position and rotation of the ellipse.
ellipse.position.set(0,0,0);
ellipse.rotation.set(0,0,0);
Apply Direction Cosine Matrix (matrix which changes reference frame).
ellipse.applyMatrix(DCM);
Translate orbit to be centered on orbiting body, where r is the radius vector of the planet.
ellipse.translateX(r.x);
ellipse.translateY(r.y);
ellipse.translateZ(r.z);
Rotate the ellipse.
ellipse.rotateZ(RAAN); // RAAN : right angle of ascending node
ellipse.rotateX(inc); // inc : orbit inclination
ellipse.rotateZ(ArgP); // ArgP : Argument of Periapsis.
Adjust for difference between center and focal point.
ellipse.translateX(-c);
Profit.
So I'm not sure if there's some kind of command flow in three.js that I'm not familiar with, or if node.js is causing asynchronous problems.
Okay. I just posted this, but found the solution for anyone who has the same problem in the future. If you want to apply things in a different order of operations than three.js wants you to (as seen in this post: Threejs Transform Matrix ordering).
Then you must use the applyMatrix function between each operation that you wish to do in a specific order.
Using the above as an example:
ellipse.position.set(0,0,0);
ellipse.rotation.set(0,0,0);
ellipse.updateMatrix();
ellipse.applyMatrix(DCM);
ellipse.updateMatrix();
ellipse.translateX(r.x);
ellipse.translateY(r.y);
ellipse.translateZ(r.z);
ellipse.updateMatrix();
ellipse.rotateZ(RAAN); // RAAN : right angle of ascending node
ellipse.rotateX(inc); // inc : orbit inclination
ellipse.rotateZ(ArgP); // ArgP : Argument of Periapsis.
ellipse.updateMatrix();
ellipse.translateX(-c);
ellipse.updateMatrix();

Procedural terrain generation with blocks

I am using three.js to create procedurally generated terrain using Perlin Noise.
I am creating the terrain using a series of blocks, but their heights along their borders are not corresponding to one another as you can see below.
How should I approach matching the height maps across blocks?
I'm using Perlin Noise Algorithm for generating heights; the problem is that the height of each point is indipendent from the heights of the near points. I've other noise algorithm, but i have the same problem..
There's a really good video on infinite terrain here: https://www.youtube.com/watch?v=IKB1hWWedMk
It's in processing, but the same concept can be applied to whichever noise library you're using - I'm going to assume that you're using Perlin noise. In which case, you need to look at the values you're passing into this function and change them based on how big your blocks are.
For example, imagine a 3x3 grid of blocks. If your middle block is (x, y), and each block is 10x10 units in size, if you move 'north' (for lack of a better term), you'd need to be getting (x, y - 10) from your noise function.
The video explains it way better than I can, but hopefully this has helped. Without more knowledge of the function you're using I can't really give a more detailed answer.
This answer will explain how to solve it for a single axis, x. It is then trivial to do the same for the y (z in three.js) axis.
The first step is to ensure the perlin noise is using the same random seed for each block. This will ensure that blocks share the same perlin noise map and so can transition smoothly between them.
The second part is to have a mapping between your block units and what is passed into the perlin noise function. For example your block x may be going from -512 to 512 units, so you get a height value for each x vertex by passing in -0.5 to 0.5 for each x vertex into the noise function.
E.g.
vertextHeight = perlin(vertexX / 1024, vertextY / 1024)
Your second block will then be offset so its edge interfaces with the first block. E.g. its x position will be +1024 more than the first block, and so will go from 512 to 1536.
So in this sense, block0 will have an x offset of 0, and block1 will have an x offset of 1024. 1024 being the block width/size in three.js units.
Finally, you need to give the same offsets to the noise function, but scaled based on the mapping described above. In this example, 512 would become 0.5 and 1536 would become 1.5 which looks like this:
size = 1024;
vertextHeight = perlin((vertexX + offsetX) / size, (vertextY + offsetY) / size)`
Therefore, the x value given to the noise function at the edge between block0 and block 1 will be the same, and so will return the same height value.

How to get Rotation around world axis Three js

I would like to get the rotation values form an Object3D according to world axises such as
roation.x, rotation.y, rotation.z
currently when I call
object.rotation.x
I get the rotation value according to the objects local axises.
Thanks.
Sorry for the thread necro.
I had the same problematic, and couldn't find a solution until I read some ThreeJS doc about Euler angles. It became clear all of a sudden.
Three.js uses intrinsic (Tait-Bryan) ordering, also known as yaw, pitch and roll. This means that rotations are performed with respect to the local coordinate system. That is, for order 'XYZ', the rotation is first around world-X, then around local-Y (which may now be different from the world Y-axis), then local-Z (which may be different from the world Z-axis).
Some implementations may use extrinsic (proper) ordering, in which case rotations are performed with respect to the world coordinate system, so that for order 'XYZ', the rotations are around world-X, world-Y, and world-Z.
Converting between the two types is relatively straightforward, you just need to reverse the order and the rotation, so that an intrinsic (three.js) Euler rotation of angles a, b, c about XYZ will be equivalent to to an extrinsic Euler rotation of angles c, b, a about ZYX.
If you need to get the world rotations in XYZ order I suppose you can do the following :
var worldRot = new THREE.Euler();
obj.getWorldRotation().copy(worldRot);
worldRot.reorder("ZYX");
// use worldRot.x, worldRot.y, worldRot.z
To rotate an object in world axis, you can use below function
var rtWorldMatrix;
function rotateAroundWorldAxis(object, axis, radians)
{
rtWorldMatrix= new THREE.Matrix4();
rtWorldMatrix.makeRotationAxis(axis.normalize(), radians);
rtWorldMatrix.multiplySelf(object.matrix);
object.matrix = rtWorldMatrix;
object.rotation.getRotationFromMatrix(object.matrix, object.scale);
}
And call
rotateAroundWorldAxis(objectToRotate, new THREE.Vector3(1,0,0), 90 * Math.PI/180);

combine rotation and translation with three.js

i'm using Three.js (without shaders, only with existing objects methods) in order to realize animations, but my question is very simple : i'm sure it's possible, but can you tell me (or help me) how should i combine several animations on a shape ? For example, rotating and translating a sphere.
When i'm doing :
three.sphere.rotation.y += 0.1;
three.sphere.translateZ += 1;
the sphere rotates but the translation vector is also rotating, so the translation has no effect.
I know a bit openGL and i already have used glPushMatrix and glPopMatrix functions, so do them exist in this framework ?
Cheers
Each three.js object3D has a position, rotation and scale; the rotation (always relative to its origin or "center") defines its own local axis coordinates (say, what the object sees as its own "front,up, right" directions) and when you call translateZ, the object is moved according to those local directions (not along the world -or parent- Z axis). If you want the later, do three.sphere.position.z += 1 instead.
The order of transformation is important. You get a different result if you translate first and then rotate than if you rotate first and then translate. Of course with a sphere it will be hard to see the rotation.

Help with simulating depth in HTML5 Canvas

Basically, I'm trying to do 3D projection on a 2D canvas with simulation of depth. As a general rule, bodies that are further away are "shaded" more and smaller than bodies that are closer to the viewer. The only thing missing is having bodies that are further away always drawn behind bodies that are closer.
Sometimes, bodies that are further away are drawn behind closer bodies, but there are always small bodies that get drawn in front of larger ones, meaning that the ones that are further away sometimes appear in front of bodies that are supposed to be closer.
I try to solve it by sorting the bodies by the z-position. The bodies array is an array of objects with 0 containing an array of the body's position, 0 being x, 1 being y, 2 being z. I first have the position of the bodies updated according to rotations in the x, y, and z axes, stored into the np value of the body object, then do the sorting, and draw the bodies. I've tried changing how the array is sorted, changing the order of the loop, but still no cigar.
Just wondering if anyone can point me in the right direction to get this 3D "engine" behaving correctly. Any help is appreciated. Some quick notes: Rotations along the three axes are accomplished using the Q/A, W/S, and E/D keys, zooming in and out of the z-axis is accomplished using the R/F keys, and the default rotation about the z-axis can be accomplished using the P key. What I'm trying to do is located here:
http://jsbin.com/aholu/5/
You're sorting on the original xyz values instead of the transformed np values. I got it to look right by switching...
bodies.sort(function(a,b) {return a[0][2]-b[0][2]});
to
bodies.sort(function(a,b) {return a.np[1]-b.np[1]});
with change see http://home.comcast.net/~trochoid/mod5.html
I don't follow all of your code so this may not be a total solution. Specifically, I thought it'd be sorted on np[2] for the transformed z value, but np[1] gives correct looking results. I guess maybe you switch these coords. Also, it looks like you're transforming and projecting the z value and the code fix above sorts on this projected z value. It seems to work out ok but I've never projected the z value itself, just use the transformed z to project xy. Anyways, Looks good!
Trochoid. I had to access from a different computer and still haven't registered, so I have to add this as an answer instead of a comment.
If you notice, the axis of rotation for x and y are rotated when you rotate any other axis (eg. if you press W to rotate, then rotate a different axis, then press W again, you will notice that the helix in your code rotates the same way). T
his is not so with the z axis. No matter how you rotate the x and y axes, the z axis will always rotate "right-to-left" (eg. in default configuration, helix is spiraling along z-axis, or axis rotated using the E/D keys, but if you rotate the helix along any other axis, rotating using the E/D keys no longer spirals the helix).
I don't know why the behavior would be different in that axis, so I would like your assistance to get that rotation working properly. Thank you.
I am aware that this is not answering the original question, but depending on what you are trying to acchieve in general be aware that there is also 'parallax scrolling' (example) (esp. in CSS3 (example).

Categories