 Welcome back to Cat from Scratch, episode 7, I believe. The topic today is triangulation. So what is triangulation? I'll talk about that, talk about why we want to use triangulation, what's the algorithm we're going to use, and then we're going to get right into the code. So that's the plan today. What is triangulation? It's pretty simple. You should already know this, to be honest. Triangulation is the process by which you can construct a set of triangles from any given planar polygonal face. So this is an example of a pentagon being reduced into three smaller triangles. So I think in general you can reduce any polygon to n-2 triangles and being a number of vertices. I think that's the case. I might be wrong. Any simple polygon for sure, anyone that doesn't self-intersect or have holes in it for sure. Now, we're not going to do with anything else. We're not going to do with triangles that self-intersect or polygons that have holes in them. That's not too important for our purposes, but there do exist algorithms to do that, and they're not too far off what we're doing today. I will say though, we're going to do 3D triangulation with this algorithm, which we're going to use an ear clipping algorithm. That's the process of ear clipping. You will be very hard pressed to find an ear clipping algorithm implemented in 3D anywhere else. This is probably going to be the most easily accessible resource for 3D ear clipping that exists. But that's that. So the question is why? Why do you want to go ahead and do this? Well, a couple of different reasons why. For our purposes today, we're obviously going to be rendering things to the screen in our CAD software. So we want people to use OpenGL and OpenGL uses triangles for rendering. So I know also OpenGL can use triangle strips in quadrilaterals and stuff, but I think under the hood, it still uses triangles. So for that reason, we need to be able to deconstruct from any given polygon or any given geometry a bunch of faces that are triangles. And we don't want to have to do it all manually. I knew in the previous video I went through and I did this all manually. It's not really worth the effort to be honest. It just takes too long. And I'm pretty good at it after doing it for all these years, but the Irish person will take a lot longer than me. So not worth it. Another reason why you want to use triangulation is because obviously we want to export things as files that can be used for printing or whatever. And those include STL files. STL files require triangles and face normals for the triangles. So we're going to need triangles anyway to export our files. So we might as well get a head start with triangulation now. Other reasons would be potentially you want to come up with a truss structure that touches certain points. This might be useful. Maybe you want to come up with a surface mesh for CFD or FDA. This is a good first pass. There are better ways to mesh faces. We'll talk about them in a future video. But this might be a good first pass or first and very simple analysis. This might be good enough. So the question is now how? I mentioned it was called an ear clipping algorithm. That's what it is. And the process is pretty simple. I'll just go through it really quick. So here are some pretty simple polygons in 2D. We're going to actually code it in 3D, but it's not too dissimilar. So the process I'll just go through it really quickly. This is what the computer will do after we're done programming. It will cut this polygon into three pieces. Now for a human being like you and me, this is extremely simple. A three-year-old could do this. This is not challenging at all. For a computer it's a little bit more challenging. Not too hard, but definitely not trivial. So what's the process? Well, the process is starting at here vertex zero, taking the next two vertices to make a triangle. The question is, is this interior angle? This is the first question. Is this angle convex? Is it inward to the shape versus this angle here at vertex three? This is an outward angle. So again, for humans this is very simple. We can say, oh yeah, this is an inward angle. But again, in 3D for a computer this is not going to be a trivial solution. We'll need to have a face normal. We'll need to have stuff like that to compute whether or not this angle is actually interior and less than 180 or whatever. So that's that. The next step is to make sure, are there any points inside this triangle zero, one, two? Say there was a point seven in here. This would no longer be a valid ear to clip off. So if those two things are satisfied, if there's no points inside in its interior angle, you can now cut off, entirely cut off vertex one from the shape and save triangle zero, one, two somewhere else. So you have this triangle zero, one, two saved, but you've now removed vertex one from the triangle. Next step is repeat that process. So now two, three, four, again this is an interior convex angle and there's no points inside. So now you can save two, three, four, if I can draw properly. I'm very bad at drawing. You probably know that by now. Two, three, four, you can cut off vertex three and now you have two triangles that have been saved from the body. And now you only have three points left. Those will always be able to be reduced to a triangle. So you have zero, two, four. Just like that you've cut off three triangles from this pentagon, again n minus two. Now this shape here is a little bit more challenging. But a similar process can be applied. So again this first vertex is the same as before. But this second vertex here is an exterior angle. It's a concave, it's a cavity on the polygon. It's not possible for this to be convex and to have the same algorithm applied to it. It won't work. So we have to skip over this. So we'll skip over vertex two as being the first point. We've got to vertex three. So now three, four, five. That's an ear, no points inside. Five, six, zero. That's an ear. Then what's it? Zero, two, and three. That's going to be an ear. And then zero, three, and five. That will be all that's left. So I have one, two, three, four, five. One, two, three, four, five. That makes this a what? A heptagon or whatever, a septagon? So yeah, that makes sense. So the question is, there's going to be math to figure out the angle, if it's an interior angle or a convex angle or not, it's not too hard. The really hard operation to do, especially in 3D, in my opinion, is computing whether or not points are inside the triangle. Because if we had a point here, like I said, point seven, that would not let this algorithm work. We had to skip this triangle as being an ear. So we'd have to be able to check, is point two in the triangle, point three in the triangle, point four in the triangle, five, six, seven. Seven is, so we have to skip. So we have to have some algorithms to compute whether or not a given point in the polygon is inside this triangle for the algorithm to work. So to do that, let's say you have, let's show a simple, two points. This is a triangle ABC. Let's say you have point P-O for outside and you have P-I for inside. And if any points are P-I, this point, this vertex, zero, one, two, has to be skipped. So you don't want to have any P-I's, only P-O's. So what's the process for this? Well, we'll have to compute some vectors, BC, AB, and CA, like this. And we're going to know a number of everything clockwise. Just know that if it was kind of clockwise, everything would be inverted from this. And we'll have normal information at this point. We'll have a face normal that will compute. We'll actually have to encode that into our structure for faces. And at this point, now that you have vector AB, hold on, let me draw this neatly because I'm going to feel bad 10 years from now watching this video. If I don't type everything, even though I can't type. So then the question is, if you take the cross product of all these side vectors and the face normal, you will have things like this. Those will be inward normals for a clockwise sequence of vertices, inward normals. And we'll call those... I'm making a mess. I'm making a mess. N2 and N1 and N3. Oh my God, this is even worse. I should have just done it by hand. And so the idea is, basically, if at the same time you take a vector from every one of these vertices on the triangle and a given point, and not just these points, but all points on the triangle. B, C. The question basically is, if I can, let's type it up. Basically, if you evaluate six dot products and you compute if they're all the same sign or not. So is N1 dot AP? You can compute N1 dot BP. You compute N2 dot BP. N2 dot CP. And then N3 dot CP. And N3 dot AP. If all these things evaluate to the same sign, as in they're all going to be either greater than zero or less than zero, all six, then you know that the point is inside the triangle. If any of these, you know, any combination of these is different signs. What do I do? What do I say? Less than one. Less than zero. If any of them are different signs, you know it's outside the triangle, which is what you want. This is probably not the most efficient algorithm, but it's my algorithm, so we're going to use it. So it does seem to work, at least in my musings. So that's the process. Again, it's not complicated. It's a little bit hard to grasp the geometry of this, but once you understand these being the expressions to evaluate, it's not challenging at all. So I will say that I went ahead and I already encoded the normal face. So we need normal faces for all our polygons. I already encoded that into our structure here for faces. So I added a normal float of size three, that being our face normal. So every face that we'll ever put in our software will have also encoded in it a normal. And additional, I had to change a couple of things. I added a, to our make face function, I added the float normal. And I also added the normals to our constructor, or whatever you call it, for our make retangular prism function. So we execute our code like we had before. It'll automatically encode for our retangular prism body face normals, which is a big whoop. So now the big question is, what's next? So what is next is the trigonometry. The trigonometry is probably the biggest thing. We don't have very many functions here. All we have is a get normal and normalize a vector function. It's not enough trig to do anything good. We need a dot product, we need a cross product, we need a function to compute the angles here into your angles. So we'll code those all up right now. So let's do them here, and then we'll talk about how they work. So we'll need, as I said, we'll need a dot product. So dot product returns a float. So we'll say float dot, and we'll take it inputs x and y. Next we'll call them v1 and v2. Now we'll call them x and y spirit. Who cares? Float x, float y, and dot product is very simple. Here it's returning, you know, x0 times y0 plus x1 times y1. You can tell I have like two words per minute as my type speed. X2, that's the dot product, very complicated. We'll also need a cross product. We can't return, that's going to be a vector. So we'll say avoid cross, we'll pass it again, float x and float y. And we need an out, so we'll say float out. That's going to be a pointer. And so what is all this? Oh my god, I am so bad at typing. So we'll say out zero equals, how does the cross product work again? So we'll use x1, no, the x1, y2 minus x2. Well, is that right? I think so. And then we'll have y and z components as well. So out one, out two. The y component is two zero zero two. And the z component is what? Zero, one. Look at the pattern here. Zero, one, one, zero. Yeah, that's probably it. If it's wrong, we'll find out, I guess, after hours of debugging. We'll also need a, oh, I know what we need. We need a way to get between the nodes. So hold on, let me show you. So we need to be able to compute vector AB given A and B, basically. Yeah, so to do that, we need a function here. We'll call this function vector between nodes. We have to pass in a head and a tail. Probably the tail first, because usually you call vectors by the tail first. So there's a struct node, node one. Hold on, we'll call this one tail. Let's be simple about this. Head, once it is float, out. So for the vector between nodes, all we have to do is compute, basically, head minus tail for three components. So we'll say out zero equals head x minus tail x. Oops, we have the same thing for y and z. We have head y, head z, tail y, tail z. That should give us vector between nodes. And then lastly, it's going to be the angle. So compute the angle of a given, you know, compute for example angle zero, one, two, about the normal of this face. Crap, hold on. Stupid Vim, I hate Vim. Anyway, so basically to compute the angle, we'll have a normal for these faces here. It'll all be the same normal as the original face. And we'll have to compute the angle about that normal for all these vertices. So we'll compute a vector like zero, one, one, two, and we'll compute the angle between those two about vector normal. So that's the process of what we're going to do. Very simple. I mean, what the heck? Oh my God. Stupid, dude. Stupid swap files that hit them so much. Turn that off, dude. Crap. Anyway, so the process here, we need a function that returns the angle. So we'll get angle functions. The angle between points. So get angle. We have to pass in a bunch of nodes. So strut node, node one, strut node, node two, strut node, node three. And we'll need an out, I guess. Burgess and angle or something. I don't know. Now I was going to pass it as a float. So we don't care about anything else. Oh no, we do the normal, obviously. So we'll say something like that. That should work. Get angle. So what we need, we'll need vector v1 and v3, meaning the vector from vertex. So for example, let's say you're at this one, two, three. This is so bad. Two, three. We want the vector two, one and the vector two, three. We've got the angle between those about n. So v1 equals, and actually we can use our previous function, vector between nodes. So we'll say vector between nodes. That wasn't on the books. Between nodes and it's tailed ahead. So we'll need node two, node two, node one. We've got cross products. So we'll say cross products, three, cross. What is it? v1, v3, cross products. Oh, I know we should do first though. This may not matter. We should normalize our vectors. We have a function for that. Yeah, normalize vectors. So we might as well just do that really quick. Normalize the vector v1. Now, basically the process for this is the determinant. You can look this up. I'm not going to waste too much time explaining why this is, but we'll create a determinant using a cross product. So we'll say float determinant equals, let's say the x, y, and the z. So we're basically dot-producting the, we're taking the determinant of v1 and v3 normalized about the vector n. So cp, not normal zero plus cp1 of 1 plus cp2. That's the dot-determinance. And then the angle, I think this is right, is going to be in the lower-term angle. The angle is going to be the arc tangent two. So we're going to take the two-component version of this of the determinant and the dot-products that should give us the angle. So at this point, we have all the trigonometric functions we need. I'm going to go ahead and put these all in our trig.h. I'll be right back. Okay, I put that in. No big deal. Now we have to do something in geometry. So we need to do a couple things. We need to be able to remove a node from a face. So basically the idea is to be able to remove node 1 after we've cut it off as an ear. That's the first thing you have to do. And then we have to be able to replace a face from a body. So you know what, these two functions first, I don't want to waste too much time. I'm going to close this up myself and I'll get back to you in a second. Okay, I put together these two functions, remove node from face and replace face from body. What it says on the tin, you can look through and see what it does. Basically it just looks through the structure and replaces things that need to be replaced or removed. At this point, we have to be able to do the actual function which is triangulating the faces. So we'll make a triangulate face function. And if the pass in, I think the pass in the body, it's important and we'll pass in the actual face on the body. Oh yeah, we need the body because we have to be able to put the new faces on there. And obviously the old face to figure out which face you want to actually triangulate. So how's this going to work? Well, we will need a target face to be saved. So we need a structure for that. How does this work? But it will be size. Sure, good enough. Oh, you know what? Oh, I forgot something. I'll be right back. So dumb. I forgot to put the stupid float inside the face structure. Malik above might be. Yeah. So what's the process for this? I'm just going to outline it into the code and I'll put up myself for running out of time. So the process is we'll need a counter for the node that we're currently on in our list. This is zero one, two, three, four, whatever. And we're going to have a loop. So we're going to say while. And we'll repeat this process of clipping off ears until the number of nodes is, or while it is greater than three. So once it goes three or below, as we have a triangle. So it should be three ideally. We'll break out of this loop. So we'll repeat that. And then the question is what we do. So we obviously have nodes zero one and two. I'm just going to cover this up quickly. So node, oops, struct node. Let me say a equals face node array. And it will be at the Ith location, but not just I we have to actually wrap it around. Because you can imagine we go around this triangle multiple or this plug on multiple times. So we have to be able to do a modulo by the number of nodes that there are. So we'll say I modulo face. Only that for node B and C. So A, B and C being zero one and two in that picture on the right. And set it to be I it'll be I plus one. And then I so we have node A, B and C. What else do we need to get the angle? So it's the float angle. And this comes from our other or other function that we just made. So get angle. I think it was called we passed in. We'll be passing A, B, C and then the base normal. So then the comes to the point is if this angle here is it greater than zero or not? If it's greater than zero, that means it's convex. We can keep going with the algorithm. If not, we have to skip ahead. So we'll say if angle greater than zero and then you want to go ahead and start counting the number of points outside. So I give you what all points are not in this triangle but are in the polygon to be outside the given triangle. So for example, for the Pentagon, we have a total of five vertices and there are three in the triangle. So we want total of two points to be outside the triangle. So we're going to evaluate these two points increment by one each time they're outside. And if that number is, you know, equals to, you know, if that number plus three equals the number of points in the polygon, we're good to go and cutting off the year. So we'll say int num nodes outside. That's it. That's a zero. And now we'll loop through the actual points. So we'll say for int j equals zero, j less than the base num nodes plus plus. So we're now looking through all the points outside the triangle. And I guess now we'll just skip over points there. And I will say if j is less than i. So if we're before, if we're before or after the points in the triangle, so you say if j less than i or j is greater than what, i plus two, if that, then we can continue on. So we don't want to evaluate that if points a, b, and zero triangle, just a points, you know, d, e, and f. So to do that, what do we do? We have to define point p. I remember down here we had a point p. So we'll say struct node star p equals, and we'll just take the face node array value for that given point. And we will be at point j because we're open around through j. And now we have to have all these vectors defined, a, b, b, c, c, a. So we'll say float a, b, 3, b, c, 3, c, a, 3. We'll also need the vectors from a, p, b, p, and c, p. And all size three. This is for our evaluation here. We're going to try to evaluate this. Have a good day with typing. And then float n1, n2, and n3, n3. And we'll see we need, oh, to compute actual values for this. So I'm going to call these s on our ys1, s2, s3, s4, s5, s6. And now we get to the actual use of our trig functions. So I'm just going to basically create all these vectors that we just defined. So vector between nodes. What do we got? a, b is a, b. How many do we make? Make six, I think. Yeah. a, b, b, c, c, a, a, p, b, p, and c, p. And now we have to take the cross products. Now we're going to get these n1, n2, and n3. So we'll say cross of a, b, and the face normal equals n1. b, c, and the face normal. And c, a, and the face normal are n2 and n3 respectively. And now this is so ts. s1 equals the dot products of a, p, and n1. s2, s3, s4, s5, s6. We'll say a, p, b, p, b, p, c, p, c, p, a, p, and 1, and 2, and n3. And then lastly, oh my god, this is such a ts. If, now we're checking if all the same sign. So we'll say if, this is such an efficient programming. s1 is greater than zero. And s2 is greater than zero. And I'll bear it back after this. Okay, so basically the plan is if all the signs are the same. So they're all greater than zero or less than zero. We're in the triangle. That's not good. So you want to break out of the loop. The thing is we don't only have to have one of these two options. We don't have to have an or. The problem is I want to make this code robust that if you have a counterclockwise node sequence, it will also work. So we have both options here. Now if we're outside the triangle, we increment the number of points and then we check if we have gone through all the points. That is, if we have counted all the points that are not in the triangle, we want to now remove node B and then loop through all the faces that have already been gone through and have three nodes and then add our new face in there with the make face, you know, with, and everything. So that's the plan there. And then once we go through all those, we break out of this, this particular project or this particular iteration, we increment I plus plus and then wrap around again with this modular. The process now is now that we've, we've got rid of the, you know, the face we don't need. We removed the node at least. Now the process is, well, we have to actually remove those. We have to take the last three nodes and make it to triangle. Basically, that's the plan here. So we have three, three nodes left. We do is we do struct node A equals face node array zero B and C. Again, this is pretty straightforward. I don't have to go through this, but I will. We're going to remove these actual nodes, A, B and C, if we move node from face or old function or moving node from face, we're moving node A, B and C. So now the face that we used to have no longer has nodes A, B and C, but we have to make a new face. So I'll go ahead and do that and I'll be right back. Okay. So now that I've got, basically I removed the nodes from that original face and I've put the three nodes, A, B and C, the last three nodes in our, in our structure into a new face and put that and replace the face that we used to have with the new face in the body. What I want to do now, that function is pretty much done. The function I want to do now is basically just loop that over every single face in a body. So triangulate, triangulate body. I'm going to just pass in a body and just loop through that function on literally everything in the entire body. That means we can be able to use this, we can use this for, you know, anything that we make. So I'm going to cope this up, I'll be right back in a second. Okay, so I put in this function here, triangulate body. It goes, basically it loops through all the faces in the body and triangulates them not too hard. And I also quickly programmed this, which is a make polygon face. This basically just literally makes a single face of a polygon. It's not unlike our old function, which is make a rectangular prism. This one just makes a single face just to give us another test case for when we see if our code actually works. Then I also went and I changed this geometry .h file to include all our new changes. And at this point, I'm going to go back into our main function and code up some test cases. So that's what we're going to do now. We don't need any of this stuff anymore. Let's get rid of that. So let's keep this print body elements. You know what? I have an idea, hold on, for print body elements. Where is that? So I'm going to actually add one more thing here. I'm going to add the normal. So print f, just so we can see it. Normal equals slash n. What is it? Percent f, percent f, percent f. Just really quick, I want to be able to just check that just to make sure that's going to be iter normal 0, normal 2. And then I'm also going to, I'm going to print some new lines here. Or like I'm going to print, I'm going to print some new lines. 2, 3, 4, 5, 6, 7, 8, 9, 10, 12. Just so we can keep track of what's going on here. Yeah, that should work. Itters a face, right? Yeah, that should work. So now that will print the body elements before we have done anything. This is after we make the prism before we do anything else to our structure. I want to type in triangulate body prism. That should automatically go through our trailer every single face. We just coded that up. And then I want to again print body elements. Then after that, I'm going to get rid of this garbage. We need to do this. We're going to create a new face. This is really fast. Struct and body. We'll call it a hexagon. Let's do a hexagon. Equals, you can make a normal. So float, normal, 3 equals, let's just do a very simple z. It's a x, y plane polygon. This is a second test case after this one. I'm actually going to have two test cases here. One being a full prism and one being just a single face. Because our prism doesn't have any fancy stuff. We need all this fancy coding to handle concave vertices and stuff. I want to make a hexagon that has a concave vertex. So just quickly code this up. I'll do that and I'll get back to you in a second. Okay, so I encoded this little hexagon here. 0, 1, 2, 3, 4, 5. So we have at least one vertex that it's outside. It's an outside concave vertex. And I coded up those points here. So this is obviously not going to compile. I know for a fact it's not going to compile. I just tried it. So I'm going to bear it back and I'm going to try and fix these problems. I'll bear it back. Okay, so yeah, that worked. Honestly, what I forgot was stupid. I forgot to do all these includes. They're missing in some, I'm not sure how that happened. They're missing in some of these Nazis. That's fine. Matt and I forgot a semicolon. And I messed up something in the print. Yeah, I actually deleted this by accident and messed up a semicolon somewhere. That's not too bad. Pretty good actually for all that coding, only a few errors. But yeah, so let's just see how that looks. That's everything, right? Yeah, so make, run. So in our first polygon or first body, we had a rectangular prism with these side lengths four, five and three with six faces. So you have, you know, zero through five. When we ran it through our triangulation function, you'll see that we had six faces with node zero through three, which is four nodes per face. After we ran it through, you'll see that we have the first face broken into faces zero and one. Second face broken into faces two and three. And this actually seemed to have worked just fine. Third face faces. So now we have a lot of places zero through 11 and they're all three nodes faces. So that seems to have worked just fine. Then our second polygon, which I can see over here. It's like this, just a very simple, like looking hexagon type thing. Actually, let me show. This is actually important to show. So basically it used to have six nodes zero through five. Now it has four faces, not one face, but four faces with three nodes each. So the first face, let's actually draw this out so we can see what it did. The first face was, you can see, node is negative one, negative two. That's this point, node zero. Node one is negative three, two. That's this one here. And node two is two, three. So it made this triangle first. That's the first triangle. So it cut off node one. Then face number one here is, node zero is two, one. That's this point. So obviously it obviously skipped this triangle here. It didn't do triangle two, three, four, so skip that. So it did two, one, zero, five, one, zero, three, negative two, zero. So this, it made this triangle next. So this was triangle one, so triangle two. Face number two was negative one, negative two, zero. That's this point. Two, three, zero. That's this point. Right? And then two, one, zero. That's this point. So it made this triangle number three. And then lastly it had this triangle negative one, two, zero. to 1, 0, 3, and 8 to 0. So this triangle's last. So 1, 2, 3, 4. So turn the hexagon into 4 triangles. Again, that is n minus 2. So at the end of the day, we have a set of functions which not only has more trigonometric capability for us going forward, but also we have the ability to triangulate any given, you know, polyhedron, you know, with a simple face into a body with only triangles. In the next video, this took a very long time. In the next video, we will be actually using our new structures to plot our fancy new triangulated polyhedron to the screen as well as exporting them out as STL files and so forth. So keeping tuned for that one, that'll be out in a few days. Thanks.