Picked up programming on Khan Academy (as should you) recently, I find their visual-based and tinkering friendly approach brilliant. One of the first "challenges", a freeform programming activity, is to draw and color shapes resembling food on a plate. Finding it far below my skill level, I figured I'd make a paint-like program with pre-stored food shapes for others to use. This turned out to be much more difficult than I had initially imagined, however (gives one appreciation for image manipulators like Photoshop and the code behind it). Dedicated to solving the problem, I focused on a specific part - making the shapes move, rotate and be the size I want them to be based on a simple algorithm.
By a pure stroke of luck (or maybe it was my subconscious nudging me in the direction) I stumbled upon Computerphile's The True Power of the Matrix (Transformations in Graphics) and knew I have (may?) to use a transformation matrix approach to my shape manipulation. Trouble was, I only had a passing familiarity with matrices from my uni days and I knew I'd have to brush up my understanding if I were to successfully code with them. Took me a while of trial and error and some basic instruction (this awesome vid) to arrive at a working understanding and then turn it into code.
Before any interesting things can be done with matrices, one has to learn how their arithmetic works, namely, addition and multiplication. Addition is straightforward - just adding the values in corresponding rows and columns. Multiplication, however, is a bit trickier, it requires the first matrix to be multiplied to have as many columns and the second one has rows and the products are the sums of products of corresponding rows in the first matrix and columns in the second. So in A*B it usually means the "bigger" matrix will be A and the "smaller" matrix will be B, the second one. A pain to do by hand to be sure. So here is straightforward code for a function Mmult that takes two compatible matrices and multiplies them (doesn't throw an error upon failure or anything, that's your job to give it appropriate m1 and m2):
var Mmult = function (m1, m2) {
var result = [];
for (var i = 0; i < m1.length; i++) {
result[i] = [];
for (var j = 0; j < m2[0].length; j++) {
var sum = 0;
for (var k = 0; k < m1[0].length; k++) {
sum += m1[i][k] * m2[k][j];
}
result[i][j] = sum;
}
}
return result;
};
With Mmult doing the tedious work I cooked up another function that would use its power to apply given translation, rotation and scaling to a number of coordinates (vertices of a triangle for now). Check it out.
var Pos = function(x,y,R,f,S) {
var TransformationMatrix = [[S*cos(f),sin(f)*S,x+R*cos(f)],[-S*sin(f),S*cos(f),y-R*sin(f)],[0,0,1]];
for (var i = 0; i < A3i.length; i++) {
mY = [[A3i[i].x],[A3i[i].y],[1]];
mX = Mmult(RotationMatrix,mY);
A3r[i].x = mX[0][0];
A3r[i].y = mX[1][0];
}
};
It took me ages just to remember to use the smaller, mY (triangle vertex holder) matrix as the second in multiplication and parse it with three rows rather than columns to begin with, that was hard, but it took another eternity till it dawned on me that I do not need to multiply my separate transformation matrices together; rather, I could compound them to arrive at the secret sauce - TransformationMatrix, especially its first two rows. Lets drop the third row and write it out as a matrix:
S*cos(f) S*sin(f) x+R*cos(f)
-S*sin(f) S*cos(f) y-R*sin(f)
Those familiar with translation matrices will notice immediately that the terms in the third column accomplish that - move the point by X and Y and also modify its whereabouts in terms of a radius R and angle of orientation f. The four S*trig expressions, furthermore, have a combined rotation and scaling effect. It also is important to point out that where the -sin is usually given as the first term in the second column, using a three-row expression of a point's coordinates it must be moved to the second place in the first column (transposed), that took me a while too. So with a (finally) correctly constructed TransformationMatrix I can apply any rotation, scale and translation I need to construct any shape be it a triangle or a fishbone. Here is the perfect circle I got with a working code:
That, however, is the more boring image my program produced while I was figuring my TransformationMatrix out. I got a reverse circle too:S*cos(f) S*sin(f) x+R*cos(f)
-S*sin(f) S*cos(f) y-R*sin(f)
Those familiar with translation matrices will notice immediately that the terms in the third column accomplish that - move the point by X and Y and also modify its whereabouts in terms of a radius R and angle of orientation f. The four S*trig expressions, furthermore, have a combined rotation and scaling effect. It also is important to point out that where the -sin is usually given as the first term in the second column, using a three-row expression of a point's coordinates it must be moved to the second place in the first column (transposed), that took me a while too. So with a (finally) correctly constructed TransformationMatrix I can apply any rotation, scale and translation I need to construct any shape be it a triangle or a fishbone. Here is the perfect circle I got with a working code:
And a reverse-reverse circle:
Note that these shapes follow a strict regularity as dictated by their drawing algorithm. The variation occurs due to varying rotations of the initial, triangle shape. Having the triangle rotate parallel to the drawing plane (in the 3rd dimension), interesting pseudo-circles emerged:
And finally I got some interesting meshes by getting the triangle double-rotate:
Just another case of an iterative process following simple rules producing sophisticated-looking forms. Play with the program here.







Nav komentāru:
Ierakstīt komentāru