ID:2240732
 
So I'm not very good at... wordsing and I don't really write tutorials, but I noticed that there are just like, none available for certain things. You can gather the information from different help threads and the DM reference but certain topics are a little hard to actually get information about. Most of the stuff I'll go over will probably have to do with presentation in some way since that's a lot of the kind of work I do, but if anybody has any suggestions for something else to cover I'd be open to suggestions.

Today we'll be talking about matrices and transforms. This sounds intimidating -- it isn't. Anyone can do this. There are a few underlying principles you should understand going in, but like I said I'm not really good with words. I am good at information gathering though so here are some smart people who have already covered the topic better than I could have:

What is a matrix?
Lummox JR wrote:
Here's a little more background on matrices without getting too much in the weeds. There are, in general, three basic transforms you'll ever want to do to a set of coordinates:

Scaling is where you multiply the x and y coordinates by a scaling factor; it can be different scaling factors for both X and Y.

x' = x * sx
y' = y * sy

Translation is simply adding to the coordinates.

x' = x + tx
y' = y + ty

Rotation is of course when you want to rotate around an angle. (The math that shows how this works is kind of interesting, but no need to get into that.)

x' = x * cos(angle) + y * sin(angle)
y' = y * cos(angle) - x * sin(angle)

There's also skew, but that can be done by rotating, scaling, then rotating back; so no need to go into it.

The interesting thing about all of these transforms is that the equations are linear. That is, x' and y' (the new coordinates) can be defined strictly in terms of a multiple of x, a multiple of y, and a constant; there's no x2 or xy or anything like that. Matrices are part of linear algebra; they're perfect for dealing with these exact kinds of formulas.

Multiplying a vector (the original coordinates) by a matrix (the transform) can reproduce all of these formulas; you just have to know how to build the matrix, or in our case BYOND can do it for you. And you can combine multiple transforms.

The order that you multiply matrices in matters; A*B is not the same as B*A; in math we say that matrix multiplication is not commutative, unlike when you multiply regular numbers together. For transforms, this means that multiplying by one matrix and then by another is like doing those two transforms in that exact order. Doing them in a different order would produce a different result. (Example: If you rotate and then translate, you end up with a rotated icon at offset coordinates. If you translate and then rotate, it's like moving the icon out on a swing arm and then moving the arm around its center pivot.)

But matrices are associative, which means (A*B)*C = A*(B*C). And in the world of transforms, that means you can combine two or more transforms together into a single matrix just by multiplying them together in the order that you want. One matrix can represent an infinite number of transforms.

What is a transform?
Ter13 wrote:
Transforms are basically 2x3 matrices.

xx yx xo
xy yy yo

Basically, when each pixel of an image is drawn, the final coordinates of the image are determined by a really simple equation:

ox = ix * xx + iy * yx + xo
oy = ix * xy + iy * yy + yo

The consequences of this should be obvious. For a really simple explanation, the matrix is broken up into 6 values:

a b c
d e f

Where a and e can be thought of very simply as scaling factors along the x and y axis respectively.

b and d can be thought of as shearing factors along the x and y axis respectively.

c and f can be thought of as offset factors along the x and y axis respectively.

However, it's not quite this simple, as a, b, d, and e are changed during rotation of the matrix via sin/cos factors.

The matrix datum provides very simple functions for manipulating matrices, however, sometimes when performing operations, if you understand the underlying math you are performing, it's entirely possible to simplify multiple operations to reduce the amount of work being done by the engine.

So now that we know what a matrix and a transform are, how do we put them to practical use? Well, there are lots of different ways, but let's just focus on some simple stuff for now. Every atom has a transform variable -- atoms are the children of datums and the parents to atom/movable (and by extension, /mob and /obj as well as /turf). But how do we actually interact with that atom's transform?



This is Bob. Bob is a superhero that forgot how to use his superpowers. How can we use transforms and matrixes to give Bob his powers back? Well, first we'll have to define a matrix:

mob/Bob
verb/MakeBig()
var/matrix/new_transform = matrix()
transform = new_transform


But that doesn't do anything yet! You may have noticed that I named the verb MakeBig(), so let's make the verb double Bob's size:

mob/Bob
verb/MakeBig()
var/matrix/new_transform = matrix()
new_transform.Scale(2)
transform = new_transform




That will make Bob twice his normal size. Note that matrix.Scale() actually takes two arguments (one for x and one for y), but if they're both the same value you only need to specify one. Now, being big is cool and all, but how are we going to give Bob back his powers of flight? Obviously you'd be a little bit higher up than those peons on the ground so we'll use the matrix.Translate() proc to visually lift Bob up in the air a little bit:

mob/Bob
verb/Fly()
var/matrix/new_transform = matrix()
new_transform.Translate(0,16)
transform = new_transform




This code will lift Bob 16 pixels higher visually. Obviously if this were a real game and not just a hypothetical you would need to do a little more than that to signify flying, but moving on. Another problem here is that people don't fly standing up, but we're programmers, darn it! We don't draw! So let's make the computer do the heavy lifting for us.

mob/Bob
verb/Fly()
var/matrix/new_transform = matrix()
new_transform.Turn(90)
transform = new_transform




This will turn Bob sideways. But wait -- he's not floating anymore! See, here's the fun part: you can add as many operations as you want to a matrix. So let's make him float, fly sideways, and, just for kicks, be twice his normal size:

mob/Bob
verb/Fly()
var/matrix/new_transform = matrix()
new_transform.Turn(90)
new_transform.Scale(2)
new_transform.Translate(0,16)
transform = new_transform




See, not so hard, is it? But now that we know we can do all of that in one shot it seems a little silly to define an entire matrix just for one operation. Luckily we can shorten that:

mob/Bob
verb/MakeBig()
transform = matrix()*2


This can actually be shortened lots of ways:

transform = matrix()*2
transform = matrix(2,MATRIX_SCALE)
transform *= 2


For more information on the syntax of matrix operations, check out the thread on undocumented matrix features at the end of this tutorial.

Alright, I think that about wraps it up for me. Hopefully this is helpful to some of you and if not... oh well. Comments, suggestions, and critiques are always welcome.

FURTHER READING:
Basic Matrix Algebra Tutorial
Undocumented: matrix()