 Hey guys, welcome back to the fourth video in this series on CAD routines from scratch. In the last video, if you recall, we had a simple 2D render of our geometry. That's nice and all, but it would be nice if we could rotate this structure with the mouse so we can see it from different angles. That would kind of be helpful, at least for me. So that's the plan on this video. To do that, it's not actually that hard, it takes a little bit of thinking, but pretty simple in the end. If you recall from last time, we had a view plane here, which was defined by U1 and U2, which are both perpendicular to U3, which is the vector from the target of our view to the center of the view plane. And I want to have a few things happen. First, I want if we can click the mouse and drag it vertically, I want to rotate the view plane around, as you would expect, the vector U1, which is the horizontal axis. Obviously for U2, if we would rotate, if we would click the mouse and move it horizontally, I want to spin it around U2. And again, if we can scroll with the scroll wheel on the mouse, I want to kind of zoom in and out artificially, obviously it doesn't make any sense in terms of we're doing a parallel projection here, doesn't really make much sense to move the view plane in and out. But what I want to do is I want to scale up the model. And we have a variable to do that from the last video. So that's the plan, implement these three things, to rotate the model around and zoom in and out, and we'll do that in this video. And to do that, it's pretty simple, we'll just be using the Rodriguez rotation formula, which is basically a simple way to relate a rotated vector per its original self around another vector a certain angle theta. So we'll get into how that works in a few minutes. Before we actually program the math, I want to just kind of show how we'll do this in the code and we'll go back to this in a few minutes. So let's open up the code for the last time. First thing, actually hold on before I do this, I want to say I also created this shell script here, make. Basically that's just going to make it easier for us to compile this later on. So that's available on GitHub, you can just use this shell script to make it a little bit easier than copying down these parameters. But in our actual code, a few things from the last video, let's comment out the right STL parts, we don't need that for this video. And then the look at vector, let's change that to 0.5, 0.5, 0.5. So we'll look at the actual center of our model as opposed to something at 0 for y, and this is just going to look better when we actually render this, I think. So now what we're actually going to do is we're just going to add some lines of code here in the render loop, this while loop here in our draw function, to handle mouse events. And that's pretty much what we're going to do, a little bit of code above and below this just to handle it, but that's the plan, we'll have some kind of mouse event handling here, and that will be pretty much the entirety of this video. So before I even do that, we have to do a couple of things, if you look here on this Rodriguez rotation formula, you'll see that everything has to be a unit vector. Obviously what that means is it has to have a unit length, and so if you recall from our last video, we had to normalize some of our vectors here, the view frame actually should be actually normalized to each one of them with these lines of code here. What I want to do is because we have to do it so often in the future, I think we should just make this an actual function by itself so we can avoid having all this code every time we want to normalize a vector. So that should be the first thing we do here, we'll just say void normal line of the vector, s in a float, the vector length 30 per sense. Now to normalize a vector, obviously you just have to divide by magnitude for each component, so we'll say void magnitude equals, we'll take the square root sum of the squareness, so we'll take the first component, vex zero, square, you can obviously use the power function for that, but I like to just multiply everything out. It's probably faster, I don't know if I don't even know, something to test. So that's the three components, all squared, square root. Now, obviously just to normalize everything, you just say each component is itself divided by the magnitude. That's that, return, and obviously this is a kind of like a pointer so we don't have to use an actual return, we can just address from the first index everything. That's pretty nice, now we'll go back down to our draw function and we'll just replace all these lines of code with the new code, so we'll say normalize vector v1, copy that three times for the u2 and u3, and we'll delete all this, and also we'll delete these lines of printf, we don't need those, which will waste space. Nice, that should just work, we'll see. Now, here's the fun part, actually doing the mouse event handling, so we will need some variables outside the loop. First among those I think we'll just talk about the things required to take mouse input, so you have to define at least an x11 x-event, so we'll find an x-event, basically an event is like you know a mouse click or you know a motion or a button press or something, that's an x-event. So we'll define one of those and then which kind of events we're looking at, well for that we have to say x select event, or great no select input right, select input. Yeah, that's probably good. And then you pass in what kind of events you're looking at handling, so from the display window we're looking at a few things, if I go back to our gimp thing we're looking at mouse drags and a scroll wheel. So let me explain how that works, let me just first tell what we need for events, so we'll need a button press mask, we'll also need a button release mask, and we will need a button motion mask, which button motion we're going to carry about, button one, so let me explain this, so basically whenever we have a button pressed on the mouse, so the left mouse button, the right mouse button, or the scroll wheel, it'll give us one of these events, whenever we release one it'll give us this event, whenever we move the cursor and button one is pushed we'll have a motion event, so long story short we'll be able to detect everything I said, we'll be able to detect the mouse being clicked with the combination of this one and this one, and then the mouse being moved after, I should say while it's clicked with this one. So that's pretty nice, and then also I should say button one is the left mouse button, I think button three is the right mouse button, and then button four and five I think are the scroll wheel four and backwards, I think that might vary for each different computer, but I think for mine that's how it is, and button one motion mask only applies to the left mouse button, but these two masks apply to every single button on the mouse, so that's pretty nice. Now in the while loop is where you actually have to handle the events, and to do that, actually pretty easy, you just say x in the next event from the display, I think only works per each display, and you store that value in the event that we just created, and then you can kind of just check about what's going on in the event, I mean you just kind of access different properties, and I won't get into the actual finer details of this, you can look up a manual and look at all the particular elements of each of these events, but I'll just focus on what we need for this particular objective, and so we can make either a switch or a series of if statements, we'll just do an if statement here, so we'll say let's say for example you want to detect if the left mouse button is depressed, you'd say if event.xbutton.button equals button one, so that basically means if we have a button event for which the button in the x button is button one, so this means a left mouse click, we will have some kind of result, so we'll say fprintf, something to do with that now, I'll just show you running, just to prove to you this is how it works, you see whenever I click I just click twice, we have two events, obviously the first one is me clicking down, the second one is me releasing, so we have two somethings for every mouse click as you would expect, let's go back down, let's talk about this, so how are we going to do this, well I guess we have a couple different events to handle, but probably the easiest event to handle would be if it's a scroll event, so let's just do that one first actually, let's comment all this junk out, and let's say if event.xbutton button equals equal to button four, and event xbutton type equals button press, we'll do something, so what does this mean, well this basically means if we have a button four which is either zooming in or zooming out, I have to think about that, and it's being pressed, so I should say whenever you scroll the scroll wheel each time you scroll it, it does two events, it does push release, push release, press release, for each time it rolls one indentation in the mouse, it's both events, so we'll say every time it does one of the events we will scale something, so for that let's just say we want a scaling factor to be multiplied by some quantity, let's just say 0.9f, so we want the scaling of our model to decrease by 10%, and then let's say if we have the same thing but for a button five we have a divide button, so it goes up by 11%, and let's see if this works, but before we do that we have to actually define what scaling is, so we'll say float scaling equals, or should we do 0.1, or just I guess just 1.0, yeah that makes sense to me, and then when we pass in the scaling value into our shaders we will just pass it, so let's honestly this probably will work, but let's just see what happens, look at that, I'm scrolling in now with my scroll wheel and we're making the model bigger or smaller, I think I might want to have it initially be 0.5, but it works out of the way, that's pretty cool, so let's let's do that, let's open up the function, let's go down to scaling, the speaker sorry that's 0.5, that sounds better than me, yeah and that works, so we've implemented this third checkbox here on our objective today, we've made the scroll wheel zoom, that was pretty simple, now these ones are obviously much more difficult, oops, so yeah we'll do that right now, so how will that work, well let's define a couple more things up on top here, so up here with the scaling let's also define a bool, which is whether or not we're clicking or not, let's say a bool left click, or left clicking, just because that does area don't have to, but now we'll define a couple of hints, so basically how this is going to work is we're going to basically have a linear relationship between how many pixels we are from the center of each axis, so if they say you're here with your mouse, there's some number of pixels between wherever you clicked and wherever you moved your mouse, both in the x direction and the y direction, and we'll use that number of pixels moved with the mouse to determine how much you rotate around, u2 or u1, or both, so we'll have to define a value for where you originally clicked and then we'll be able to track where your mouse currently is with one of those events, so we can calculate those dy and dx, so let's make some values for that, we'll say int x start and y start, now we'll need a bunch of floats, so we'll need some floats for the actual angles themselves, so we'll say theta u1, theta u2, that will store how many angles you want to rotate around, u1 and u2, now let's create some more floats for the actual vectors themselves, so I think we will need rotated versions, so we'll need three components in the u1, new vector, u2, new, u3, new, we'll need, I think we'll need some intermediate ones because we have to rotate twice, once around u1, once around u2, so we'll create intermediate ones, we'll say pending, this should be fine, that's probably all we need, if not we'll add some more later and now we have to add in everything within, so first thing we'll do first before I forget is we'll change these values here to new, we don't want to use the same old u1, u2, and u3 for our renders, we want to actually change that, so we will do that, so I guess our first task will be in this if statement here, we'll say if the event is x button, button 1, and it's a button press, so it says below, we'll say event x button type equals, but hold on, I messed this up, this has got to be a double equals, surprised that it even worked, actually it makes sense that it would work, so anyway type equals button press, this will basically, if I, whenever we click the left mouse button, so we want to do is you want to say left clicking equals true, or equals 1 I guess, maybe more sensible, and then we'll set the values for x start and y start, so x start is just going to be event x button dot x, and as you would expect y start is stored in the event x button dot y, so now we have the initial point here, this point here, the left mouse button being clicked, now we will say if it's released, so if equals button 1, and the type is button release, we want to set this to 0, and we have to define some, we have to basically set things now at this point, now we've, ideally we've highlighted everything here, we're just, you know, sending in the values for next time, so I guess we'll just say u1 equals, or I guess we'll just say free-shrine, u1 equals u1 new, 0, same thing for all the components, basically this means if we ever release the mouse button, we'll be able to continue using u1, u2, and u3 for the current view frame, so whenever we let go of the mouse button, u1, u2, and u3 will still correlate to these three vectors here, we can preserve that identity, and yeah, that's pretty much it, we're going to need this, I don't think we can get rid of these three lines here, two lines there, now here's the ultimate question, how can we create these vectors, u1 new, u2 new, and u3 new, that's what we have to use this front tree guys formula, so we'll create another if here, we'll say if, left clicking, and we have a motion event, we'll say event x motion type equals motion notify, that's the event type for motion events, here's where we'll do all the actual math, a lot of math to be done in here, so what first, well first we want to compute the angles, so going back to our little drawing here, we want to compute angles around u2 and u1, so we'll say theta u1 equals something, and what will it equal, well it will be some subtraction of where we are now from the x star, how much we've moved in the x direction, we will divide that by the height of the screen, so we'll say float h, or h is what we call the height of the the rendering window, and then we'll have to correlate that with some amount of degrees of rotation, so for me, I think I think we'll do 15 degrees, so 15.0 f and we'll just divide it by pi, so we can use tiny cosine, so how can you give this difference function, well in some ways we have before, we'll just say event dot, it means x motion dot x minus x star, this will give us theta 1 and then theta 2, all we have to do is change a few things, we'll change that y value, x value to y, this x value to y, and this h to a w, this will allow us to calculate the opthetas around the two axes that we need to calculate it around, that's pretty nice, now here comes the actual math, so let's open up this trajectory, let's take a look, so it's pretty simple, so here v is the initial vector, v-rot is the rotate diversion of that vector, and k is the vector you rotate around, so for us what we have to do is, say you want to rotate first around u2, what we have to do is rotate both u3 and u1 this many degrees, or this many radians around u2, so just to plug in this equation, I'll do it really quick and I'll get back to you, okay so I put in these functions here to rotate u3 around u2 by theta 2, as well as u1 around u2 by theta 2, I pretty much just copied this function here directly, I did define a value here called mag, which is the dot product of k and v, and for that we have to come up here and define mag outside the loop, and I realized I made a mistake over here, where is it? yeah here when I evaluate theta 1 and theta theta 2, I should be, theta u1 should be the y-distance and theta u2 should be the x-distance, so that's a mistake on my part, and then I've normalized everything using the same function as before, so u1, u3, and u2 we didn't change, u2 stays the same, right, u2 doesn't change when we spin around u2, now this should work, one more thing we have to do though is we have to actually define u1, u2, and u3 new out of the loop, because right now it's going to call u1, u1 new, u2 new, u3 new into the function, we can't have that, but we define this, so we'll say u1 new, zero equals u1, zero, and we'll copy that a couple times, I'll be right back, so it looks like this, so u1 new equals u1, u2 new equals u2, u3 new equals u3, setting it in the first place, so when we call it for the first time, it actually renders, so let's see if this works, now this should rotate around the u2 axis, if we move the mouse from left to right while clicking, and there you go, it does, obviously the vertical motion doesn't work yet, we'll get to that in a second, but we do have complete ability to spin our model around, that's pretty nice, let's go on to the second part, we're just rotating around u1, so if we do that, let's do a couple things, first let's get rid of this, we don't need to make these intermediate values here, but what we do need though is we have to set things as an intermediate result, so we want to define u1, I guess u2 and u3 as the pending types of the values, so we'll say, actually let's copy this, just change some of the variable names around, I'll do that and I'll get back to you, so it looks a little bit like this, so we have u1-pend equals u1-new, u3-pend equals u3-new, then u2-pend is just u2, we haven't changed u2 yet, but we'll do that in this, this time around, and actually it should be the same as before, they need the same functions, everything's exactly the same, but instead of it being around u2, it'll be around u1, so we'll say rotate u, I suppose u3 around u1 by theta u1, and then we'll go to rotating u2 around u1, doesn't matter really, but finally we'll pick this order, and actually all we have to do is we have to switch out 2 for 1, right, so up here we're doing u3 around u2, now we're doing u3 around u1, so 2 goes to 1, so I've got a 1, I've got a 1, I've got a 1, these ones all have become 1s, those one can stay 3, this becomes a 1, 1, 1, this becomes a 1, 1, 1, this becomes a 1, 1, 1, 1, 1, 1, all work, u1, u1, u1, that makes sense to me, however we have to change these values, instead of just being u1, u2, and u3, we have to make them actually say pen, so change them all to say pen, and I'll do that, and I'll be right back, okay, I've done that, so I've changed all the values to the pending version for this first set of equations, now I'll do the same thing for the second set of equations, so in this case we're just replacing, actually we're just swapping them around, so u1, u2 will just change places, I believe, yeah that makes sense to me, so this will say u1, but this will say u2, u2, so I'll finish this, and also I'll go back and I'll change the multiple pending versions, and I'll be right back, okay, I've done that, now we have to normalize once again, but instead of u1 and u3, it's actually going to be u2 and u3, and honestly this should just, I don't know, we'll see, this should just work with us, so let's make, let's run, I hope this works, yeah that works, so as you can see, now we can rotate our render with the mouse, and we can still zoom in and out, so scroll wheel in, let's zoom in, scroll wheel out, let's zoom out, and we can rotate the model, so that's pretty cool, now you can actually see how well we're able to project this onto the screen, it looks pretty nice to me, so the next video I'm not sure what we're going to do yet, I'll think of something cool, probably something about the geometry here, maybe like the face normals or something, or picking points on the on the screen, we'll see, I'll have a new video out in a few days on this, so anyway thanks for watching.