//****************************************************************************// //************ Composing 2D Transformations- January 11th, 2019 *************// //**************************************************************************// - The 4th row in a class...it is a thing --------------------------------------- - Okay, so on Wednesday we talked about the 3 major types of transformations: translation, scaling, and rotation - Translation, sadly, worked out to adding 2 vectors instead of multiplying matrices...but then we found out that we COULD express it as a matrix multiplication using "homogenous coordinates!" - "This is how graphic libraries handle transformations internally, as matrix multiplications" - Now, let's suppose we have the following problem: we have a little picture of a house (represented by Mr. Pentagon), and we want to rotate our home by 90 degrees - If we just apply our rotation operation, we'd rotate about the origin - But what if we want to rotate IN PLACE, so that we're rotating around the center of our object? - As it turns out, OpenGL has NO idea what we mean by the "center" of our object - so how do we do this? - Well, if WE know where our house's center is, we can do it by moving our house to the origin, rotating it, and then moving it back - i.e., we're TRANSLATING it! - So, the process would look like: 1) Translating our house so the center is at the origin (T1) 2) Rotating our house however we want (R) 3) Translating the house back to its original position (T2) - So, in matrix form, rotating our house around a point (a,b) by 90 degrees would look like: translate(-a,-b) -> rotate(pi/2) -> translate(a,b) T1 = [1 0 -a] , R = [0 -1 0] , T2 = [1 0 a] [0 1 -b] [1 0 0] [0 1 b] [0 0 1] [0 0 1] [0 0 1] - However, REMEMBER that matrix multiplications happen right-to-left - so, we have to put the first operations at the END of the matrix multiplication - "Think of these as functions, and the idea of function composition - the innermost function will get called first!" - Therefore, to rotate our point P to its new home: P' = T2( R( T1(P) ) ) = (T2*R*T1)*P - "DON'T FORGET THIS; you should always read the matrix operations right-to-left, from the vector backwards" - Notice here, though, that our matrix multiplications are associative - and, therefore, we can combine ALL of our operations into a single matrix just by multiplying them together! - "So, if we have a polygon with 5 million points or something, we don't have to do 3 separate intense calculations to rotate it - we can just do 3 quick matrix operations to combine our transformations, then do it once!" - So, the key points to remember: 1) Operation order matters - If we rotated before translating, we'd end up with a completely different result! 2) We can combine/compose multiple operations into one - So, going back to our triangle from yesteryear, how would we translate it 2 units to the left, and then double it in size? Well, it's a translation and a scaling - SO, writing out the handy-dandy matrices for these: T = [1 0 -2] S = [2 0 0] [0 1 0] [0 2 0] [0 0 1] [0 0 1] - Combining them (don't forget to reverse the order, so translate happens first!), ST = [2 0 0] * [1 0 -2] = [2 0 -4] [0 2 0] [0 1 0] [0 2 0] [0 0 1] [0 0 1] [0 0 1] - Great! Now, let's apply our combined translation-scaling operation to each point in the tri-angled figure: [2 0 -4] * | 1| = | -2| [0 2 0] | 1| | 2| [0 0 1] |{1}| |{1}| (...do for the other 2 points...) - *had to add the 3rd coordinate to the vector to work w/ homogenous coordinates - Doing this in the OpenGL API would look something like this: glScale(2.0, 2.0); glTranslate(-2.0, 0.0); draw_triangle() - "This looks backwards, because we have to call our functions in the SAME order as the matrix multiplications!" (i.e., the last-called operation is done first!) - Why doesn't the library just handle this re-ordering for us? We'll see an example on Monday where having control over this is useful - Okay, we've established that in general, operations aren't commutative - but are ANY operations commutative with each other? - Well, translations DO commute with other translations (it's the same thing as commuting additions) - ...but translations don't commute with rotations, uniform scaling, or non-uniform scaling - all of these apply vice-versa as well (e.g. since translations don't commute with rotations, rotations obviously also don't commute with translations!) - Rotations commute with one another in 2D, and with uniform scaling! - ...but not with non-uniform scaling, and 3D rotations DON'T commute with one another! - Why doesn't it work in 3D? Imagine rotating 90 degrees around X, then Y, and vice versa - it just doesn't get you the same answer - Uniform scaling is really just multiplication, so it's commutative with itself AND non-uniform scaling - ...but not with the other stuff - On Monday, we'll start exploring the "matrix stack" and getting a little bit into OpenGL - until then, have a great weekend!