Transcripts
1. Introduction: Hello everyone, My name is ideal and this is recursion, a course all about influenced repetition. Recursive functions are simply another way to repeat a block of code. However, with some influence from the results of previous iterations. Also the preferred two loops if the iterative code is too complex as recursive functions are simpler and more succinct in this course will depend on recursive functions to create three fun examples. One with circles and squares, and finally, one with triangles, will go over three different approaches to execute each example helping you get a full grasp on the subject. The first approach will be on the simplest form of recursion. While the second third examples would include objects and arrays to have and more interesting devices. I can't wait to see to learn all of that maturity cell listening.
2. What is recursion?: A recursive function is an iterating function that calls itself until a condition is met. Very similar to loops, however, some functionalities are dependent on previous iterations. And to execute them with loops, they require storing information outside of them. That's when recursion is the key. It's important to note though that recursive functions aren't always fast and memory efficient compared to loops. So use them with caution. Some very famous examples and generative art is recursion such as fractal trees, the recursive triangle and caught curve. In this course, we'll be creating three different artworks using circles. First, rectangles, and triangles last, using a slightly different method for each. In the next chapter, we'll create the circles example to see how recursive functions work.
3. Example: Circles: In this example, we'll see how to create a recursive function to make this artwork. To start, we'll create a regular function that draws a circle and color it at any position. We specify one called Very simple. I'm going to make the fill random. And it's only going to be grayscale. And for the circle is going to take x, y, and a size because we want to specify the size outside of the function plan. We call it with arguments. I'll make them float. If we call it inside of the draw function, we'll see that it's presenting the circle at the position specified. So let's make it appear in the center on the screen. And let's give it a size of a 100 maybe. And just note that the size of the circle is not the radius, it's actually the diameter. So they take the whole width of the circle. And let's stop the loop first because this is going to loop forever. Mainly because we don't need to loop the draw function since we are going to create a recursive function, which is going to be an infinite loop basically. But we're going to add a condition later. So let's run this. And we have a circle with a random color each time I run this. Cool. Now let's add one more thing to the function. Let's call it inside of itself though. And this is what's going to create the recursive function. Of course, we have to add a condition because if we don't, we'll get an infinite loop that can freeze your computer. What's cool about processing, however, is that it detects that a block of code is infinitely running and presents an error stating that. And all you have to do is stop it. So if I run this right now with an arbitrary position, Let's make it 10, ten to a 100. Froze a bit, but it produced the error. The sketch is attempting to much recursion. So let's add a temporary condition called level that takes its value from a defined parameter inside of the if conditional will decremented. I'm going to add it over here. Again, this is very similar to loops. We'll start with three for level over here. And we'll check that the inner call will only run when level is greater than one. And we'll decrement level so we can at some point reach the one and stop the recursive call for the function's argument. I'm going to change these hand-coded numbers. And we'll do something relative to our first or base circle, which is drawn over here, or rather over here. Let's say x plus 100 and y plus hundreds. And for size, we'll do have the original circle size. This means that each time this runs, we're having the circle's diameter. Now because functions are nested, that runs slower than a regular function since each function is calling another one inside of it. And only until the last function that meets the condition ones will the result of the first call be presented? That's the one that's outsides of the function. If you print a value outside the function below the main call, you'll see the final result. However, if you print a value inside the function, let's say or V here, you'll see it for every call, very similar to loops like I mentioned in the previous video. If we run this right now, we'll see the other circle at a 100 points, right? And a 100 points down from the original circles position and at half the size. Before I run this actually made a mistake and put level the decrement after the function. I have to call it before, or I have to decrement before because this is taking the level as well. So be sure to do that. Now if we run this, actually, let's start with two for the level because I want to show you one circle before I start creating more circles. Now if we run this, we'll see the other circle at a 100 points, right? And a 100 points down from the original circles position. And at half the size, Let's increase the level to three. Now we have two more circles. So at level one or at the base, we have a circle at width divided by two and height divided by 2 for x and y with a size of a 100 for the diameter. But then the second one is half that size, so 50. And it's 100 pixels rate and a 100 pixels down from the original circle. But then the other circle, or the third circle, is half the size of the second circle. That's what recursive functions do. They take the values from the previous call and they apply whatever changes they need to apply it based on those changes. So for the third circle, the second circle is actually the basis of its calculations. So x plus 100 is actually referring to the second circle. And the y and y plus 100, it's referring to the second again, and then it's having the size of that second circle for the third one. If we used loops for this, we're, we're going to have to store that information. So story, the x and y positions and the size outsides of the original one. Or we weren't going to have to do more calculations for a to work this way. Now hopefully this makes sense. So let's create our artwork. This is a very simple artwork that looks very complicated. The idea is to draw four circles on top of each previously drawn circle, creating what looks like for round corners to a rectangle. Each circle is moving to one of four directions from the center of the previous circle by half a diameter with a size of diameter divided by two. And because we are going to draw four circles on top of the previous circle, we're going to need for function calls. So let's copy this one over here. The first one is going to go QX plus half the size. And then for y it's going to stay where it is. And for the other one, it's going to decrement half the size of the previous circle. And for y it's going to stay the same. And then we're going to do the same thing with y for the other two. So I'm basically moving right and left. So one circle is moving rate, the other is moving left, but on the same Y level. And then for the other two were moving up and down, but we're staying on the x-axis where we are. So this is going to be size divided by two and this one as well, but this is going to be minus instead of plus. Let's run this with a level of 2 first. So you can see what is happening for the next generation. So as you can see, we're moving top and bottom and then right and left. Let's increase this to seven this time. This is very convinced the reason why this is happening is because the base size is very, very small. So I'm going to make it much bigger. So let's do with divided by 2 or even width divided by 1.5. And I also rotated and translated the whole thing. Instead of putting it at the position of width divided by two and height divided by two. So let's do that. The only foreseeable issue is that we don't know the level where this looks presentable because this is very condensed. The more levels, the smaller and less visible the circles are. We'll have to deal with trial and errors in order to get to where we want to be. Not a big problem though, if you are okay with that, but I'd rather know visually when to stop. We can fix this by changing the condition to check if the size has reached a certain point. Let's say we don't want any circle below 34 a diameter because they can somewhat visualize, that would look fine. So we can remove the level parameter. And instead, we can just check for the size. So if size is greater than 30, so as long, as long as it's greater than 30, we're fine and we can remove all of these arguments inside of our function calls. And now we have a more succinct function that gives us a bit more control. So let's try this one and see how it looks. Okay, So 30 is too big, so we can make it 10 for the size condition, or maybe 15. Now we can clearly see the design to finalize it, we can add some color to it using a color array. So let's add that color array. I'm going to just copy and paste it over here before I the setup function. And down here, and instead of just randomizing a grayscale value, I'll get a random position from the colors array or calls for how I called it. And we have to cast it into an integer because random produces a float and we need integers for array positions. And we need a random position from 0 to the colors length. But since we are casting it into an integer, it's going to actually rounded down to one less than the colors length, so we'll never be out of index. And voila, we have a color design. You can also play around with this and maybe have more color presented more than the others. So changing the probability of one color appearing more than the others, I managed to create something that looks more like a galaxy or a bunch of stars on the sky. That looks really, really cool. And it's a very simple design that was created only out of circles and one function. There is one more issue with this approach. However, when exporting to a vector format, here it will matter as much because you can see the previously drawn circles and that's the element that creates this interesting visual. But for our upcoming examples with squares and triangles, will need to adjust our code to bit to avoid overlapping shapes that won't be needed for the end results. Let's head on to the next chapter to know more about that.
4. Recursions with ArrayLists: In the previous example, the recursive function is drying shapes on top of each other, making the Canvas crowded with shapes. That's called for that example. But what if we want to use the recursive function solely to influence each iteration bytes previous generation. Let's say we want to cut each square on the Canvas, 24 other squares. And we want to iterate that three times, starting with one square, that should give us 16 squares. But when exported and opened in Adobe Illustrator, we get 21 squares, the first square, the next four squares, and finally our desired 16 squares. Now we can fix that if we incorporate ArrayList, we can simply store the previous generation of shapes in the array and replace it with a new generation, resulting in only 16 squares in the final design. Let's get on to the next chapter to see how that works.
5. Example: Rectangle: For this example, we'll draw a randomly divided squares. Let's start with our previous example, changing this circle over here with square. And also in every call. And the parameters are going to stay the same, but I'm going to add resolution over here. I'll make it an integer. And I'll just call it rises. You can call it level generation, whatever makes sense to you. We'll also change the shape over here, which I forgot to change to square. And now we can adjust the positioning of these parameters over here, or these shapes are calls to be at the four corners of each drawn square on the screen, though will be x and y, which is the top-left corner of the first square. And then for the second one, you can either choose to go right or go down because we're going to go in all of the other corners anyway. So yeah, it's up to you where you want to put everything. So for me it makes more sense to go to the top left corner and then move to the right and then move down from the top left corner and finally do the bottom right corner because that has more texts in the parameters and I don't like it to be in the middle where it's very long. So this is going to be x plus size divided by two. And y is going to stay the same because we're still moving horizontally on the same y positioning, but we're moving to the right by half the size of the square because we're cutting it by half. And then 4 third 1, where moving on the y-axis, but we're staying where we are at the x-axis. And then for the last one we're moving for both. So plus size divided by two for x and y. And I'm also going to remove the translate and rotate over here. For this, it's going to be the full width. For the condition over here. I'm going to change it to red. And I'll say if Raz is greater than one, and then I'll decrement over here by one. And then I have to add it over here in all of the calls just to remove the errors. And we don't have any missing arguments and elegy the same over here. But here I have to actually put in a value. So I'm going to start with a resolution of two. And now running this, I have four squares. So if I iterate this two times two, that's a resolution of sweet, then I get the 16 squares that we talked about before. And now to show you the problem that I'm talking about when exporting to Adobe Illustrator, I'm going to export this and open it in illustrator and come back. Okay, So after I released the mask, we should be able to take this apart. So if I remove this right now, there's still a square below it. And if I remove this, It's the square. From the first iteration. And then if I remove this one, this is my base square. So I have all of these squares on top of each other. And that's not ideal if I want to export this design, to use it in Adobe Illustrator to maybe create a thumbnail like I did with this course because this is going to make my file size much bigger and I'll make a tedious, especially if you have way more squares to clean it up. Because then you'll have to remove everything below your main design in order to keep what you need. So in this chapter we're going to fix this problem using ArrayLists. So let's go back to processing and do that. Now we're back in processing and let's add our ArrayList over here before our setup function. And now our ArrayList needs a type. And that's where our class are. Simple class come in. So I'll have to create a new tab, and I'm going to call it just simple SQ or whatever you wanna call it. But I'm going to call my class skew for short first square. And since over here we had an x, a y, and a size. That's what our square class is going to take here in the constructor. And we'll have a display method that will draw the square. So whatever is in here, we'll take out and we'll put it over here. So now we have our shape ready to use inside of our ArrayList. I'll simply call it squares and I'll initialize it. That's all we need to initialize the squares ArrayList. Before in our circle example, we plainly call the recursive function in the draw method. However, because our recursive function isn't drying the shapes anymore, we'll need to approach this differently. I mentioned in the previous video that ArrayLists are used to save the previous generation, to then replace it with the new generation. We already have the array for the previous generation at the top. Let's create the new generations array down in degenerate or in the draw a square function, which is basically the recursive function. So I'm going to just call it Tim. You can also call it next for next-generation, it's up to you, whatever makes sense. And now we can remove all the parameters over here as we don't need them anymore. So I'll just change all of this to only take the resolution. And then in here as well. But before I move any of that, sorry, can copy it and paste it to our new calls or to our new instances of the class. So let's create those, will need four of them. And we'll add them or append them to the temp array list over here. So we'll just do temp dot add. And I'll do new instance of the square or Skew class. And then I need something here to call the previous generation. So the previous generation is in this ArrayList. So I have to loop through this ArrayList to get every square from the previous generation and manipulated over here, or have it over here to create our next generation. So I'm going to create a loop inside of this condition over here that will loop through the previous generation. So I'll just say four square of type SQ, or let's call it just as cube, just to be concise. So for each square in squares, I'm going to do the following. So I'll do the square x position and the square y position, and then the square size divided by two. And then I'll copy this one and paste it three more times and then edit it or rehear to be the same as the ones that we did before. And now we can remove all of these calls. Okay? Now we have our temporary array populated with the four squares that are going to be our next generation. But now how do we put this temporary array or next-generation into the first-generation? We can simply outside of the for-loop because we don't want to loop this. We can just say squares, which was the old generation, replaced by temp, which is the new generation. And then we'll remove raise from here. And I'll just add it over here. And then we have to call this function inside of itself because we're talking about recursive functions here. So this is going to be draw square and it'll take the new resolution over here. So now we have our next-generation and then we replace the previous generation with the current generation. And then we decrement the resolution so we can go to the next iteration and then we call it or recall the recursive function in here with the new resolution. And all of this is inside of our if conditionals, so we don't loop infinitely. Now if you go up here, we have a missing step. Because before we use to draw the square or V here inside of our recursive function. But now we're only adding all of these instances of this square, but we're not really displaying it anywhere. And now this recursive function is only creating the new generations. So it's creating the instances and it's saving it inside of the squares array on the top. So we have to display it somewhere. That can easily just happen over here. So I will just do the same for loop that we did in here. So I will just copy and paste this one. And display it. So this is simply displaying whatever is in the squares ArrayList. So it's not really doing anything to the recursion part. But if we were in this right now, it's not going to work because we're taking the previous generation over here and then we're doing these calculations relative to it. But we don't have a first-generation yet, so we have to add one to the ArrayList over here. So we'll add that in the setup function. You can add however many squares you want to initialize this design. I always start with one big square that is covering the whole canvas. But maybe you want to start with a square that is just in the center of the canvas, or maybe you wanted just in the corner. So it's up to you how you want to start the design. But following, or the following generations are always going to be the same. They're always going to cut each square on that screen or on that canvas the same way over and over again. So I'm going to start the same way we started before. So I'll just add that to the squares ArrayList. So I'm going to create a new instance of the square to be at 000 and it's going to be the width of the canvas. So this is going to start our design. And then we're iterating two times. It says three, but it's actually iterating two times because we're saying to stop when the resolution is greater than one. So we're not really reaching one for this. And then we're creating the next generation from that first square. So it's going to be Foursquare's and then 16 squares. So now if we run it, we actually have 16 squares. And if I go and open the new file again in Illustrator and released the mask and start removing. I don't have any squares below it. So we have only the 16 squares. Also. You don't have to worry about these because these are the strokes as I have a stroke on my design. But you can just simply have no stroke and then do the stroke here in Illustrator. Okay? Now that we have created this and we have solved the issue that we had with the previous design. I want to make this a bit more interesting because now it looks just like a normal grid. So to do that, I'll add a float cold are inside of my loop, inside of my recursive function drawSquare. And it's going to be a random number from 0 to one. So floating number from 0 to one. And then I'm going to check if this random value is less than 0.7 or 70%. And I'll add all of these instances in here. So I'll only cut the square four times if it's less than 70 percent. So there's only a 30 percent chance that a square is going to be cut four times otherwise. So the 70 percent is going to be the same square. So it's going to draw the same exact square without changing its size. So it's going to be XY and the same size. You can also make it a 5050 chance, but I'll just start with a Sony 30 chance or a 37 chance here. Okay? And once that is run, I have some squares that are not divided and some squares that are, this is not going to show up clearly unless we increase the resolution. So I'm going to increase it to seven and we'll run it again. So yeah, this is pretty cool because now we can see that it doesn't look like a grid. Some squares are actually not divided into four other squares and some are. And the more you run this, the more interesting results you get. You can also try this with rectangles because that will produce even more interesting results. You can also add or conditions tear if conditional. So maybe you can say I want to give it a 30 percent chance that it's going to be cut in half horizontally versus being cut vertically. And also add maybe another one to cut it into four different rectangles. So you'll have more variations in the design of the end. Play around with the probability, play around with the shapes and see what happens. Next. We'll see how we can enhance on this example to create more interesting shapes.
6. Recursions with complex objects: The upcoming approaches merely enhancing on the previous example. Instead of using a simple class that only draws a shape, will make a more complex class that defines the shapes rules. This time we'll work on each point of the basic shapes individually, giving us more freedom to play around with the layer. This method is usually used if we want to randomize the position of 1 while maintaining the positions of the other points. It'll be clearer once we start the example. So let's do that.
7. Example: Triangles: In this example, we'll go beyond to what we did in the last example and define the shape inside of our class, treating each point as a separate entity. Let's begin from where we stopped in the last example and change a few things will work with triangles. So the first thing to do is rename our class and tap. I'll do Rename and I'll just call it try, short for triangle. And then to also name this one a try. And I'll change this one over here. And inside of our class we need to change the shape used in our display method that will become a triangle. And they take six arguments and x and y value for each corner. Let's define the points so we can add them to the shape function. Since we'll have plenty of points here, we'll use p vectors instead of floats, which means we'll have three corners combining X and Y for each. Instead of our previous floats will type p vectors over here. And we'll have a, b, and c for the corners. And I'll move this to the parameters over here inside of our constructor, but with an underscore for each name to differentiate between the properties and parameters. So an underscore for e-tron after will assign the parameters to our properties like we did before. We can now use them in our triangle function. We have a dot x and Ow.ly and so on. Before we finish our class or change anything from the previous example. Let's just all the errors in our main tab. Well, first change the ArrayList type to try. And we'll call it tries instead. I will after comment-out this first square because we're going to substitute a later with a triangle or a few. And now we'll change our recursive function definition to draw triangles. And I'll change its color over here in the draw function. Now inside the recursive function will change the type of the temp ArrayList. And the for loop will iterate over the tries ArrayList instead. So for each triangle inside of the triangle or the tribes array, and I'll do the same over here, will come in these objects because we'll use the same approach but with triangles this time. Lastly, you will change the assignment of temp two tries and change the recursive function call here. To draw triangles. Now we should have no errors and we can begin drying the triangles. Let's head back to the trie class. Before with the squares, we relied on the square function to determine where to end based on the start position and the size we define as. That's how the function behaves. But for triangles, you have to decide where each point goes. I know that I want each triangle to be cut in half into two other triangles. So basically drawing a line from point a, let's say, to the midpoint between corners, BNC, that will visually create two new triangles that chair aside that we'll call AD. Knowing that points or corners a, B, and C didn't change positions. We can transfer a copy their positions to the new triangles p vectors. We can call the copy method on a vector and it'll get every component we need from there. Let's do that. In the class, we'll define a method called point a. And it's going to return a p vector, which is going to be the new copy from the original point a. And we'll do the same for points B and C. Let's now define the fourth that the two new triangles share. We'll call it point to D. And this method will also return a p vector. We said that this is going to be the midpoint between B and C. And easy way to find the midpoint along a line is to use the alert function. The slope method interpolates between two vectors based on a percentage. So if we say alert by 0.5, that's 50% from the first vector to the second alleles. This static method from the P vector class, as I want to create a new object and not transform one of the old ones. Meaning if I use c dot b by 0.5, it will move the c vector to the 50 percent position between C and B. However, if I use the static method like so, Vector CB and 0.5, it'll create a new object that we'll call d. Let's start that in a new vector called D. And after returning, it probably takes a bit of time to wrap your head around it, but that's totally okay, especially if this is your first time working with recursions and p vectors. All of this will make more sense when we create the triangles in the main tab. So let's go and do that to see how the triangles are drawn. Let's start by defining our first triangle up here. So instead of this square object that we use to have, I'll just do tries dot add, and you try object. And it will require three points. So I'll create three p vectors over here. And I'll call them a, B, and C. And then I'll initialize them over here. So a is going to be at 00. And then B is going to be at width and 0. And C is going to be at 0 and height. And then I'll use them over here. So he basically cut the canvas by half diagonally because we had a square. So running a diagonal line from the top right corner to the bottom left corner, we'll create this triangle. And the first is going to be on the top-left corner at the zero-zero point or the Canvas. So if we run this which you see the triangle. Now let's cut this into two triangles to create even more triangles recursively. We'll do that inside of our for-loop, inside of a recursive function because we want to do that for every triangle existing in our ArrayList, since the functionalities are happening inside of our class, inside of our four methods that we created, we'll create four p vectors over here, a, B, C, and D. And each will call the method associated with that point. So now what this is doing, it's going through every triangle in the ArrayList, and then it's creating four points for that triangle. The first one is going to be the same exact copy of the original a point. The second one is going to be the same as the B point and the third one C. And finally, the D is what we created interpolating between B and C. So we already did all the functionalities inside of our class 2. Now we're only calling it over here for each triangle. So let's substitute the square objects over here with the triangles object. I'll do it without the randomization first. So I'm not going to do the if conditional. Since the new triangles share the side AD and the points for each triangle start with a for the main point. As we are cutting the side BC from point a will have to start the new triangles with d. So this will be considered new a point. And then a and B for the first triangle, and a and c For the second triangle for the two other points. So if I write this, it'll look something like this, and we'll save it inside of our temp ArrayList. So it'll be a new triangle. And that triangle will have a point as d and then a and B for the two other points. And this will have, it's a point again as d because they're sharing this D point. And then a and c for the two other points. So for the next triangle, this is going to be the a. So this is where it's going to go to cut these or this side. And this is going from point D or what's going to be point a and cutting this side by half. Let's run this with a resolution of 2 first to see the second generation of this design. And I'll leave the strokes for now so you can see how it's being cut. So at this point, this is point a, the new point a would choose the old point DDA we created. And for the next generation, this is going to be, or the red triangle is going to be cut from this point to the midpoint between its other two points. And the same for the purple over here. So if I do the third generation, but it's going to change color because I have it as random colors. So I now have these cut. And if I run it again, so it changes colors, you can see that it's randomizing it. Now that we see how it's working, we can add more generations by increasing the resolution to, let's say 10. And this already looks cool, but it's something that we can do without the recursion. So let's change something inside of the class to make theirs or to randomize this a bit further. So go back to the class. And now instead of cutting one of the sides by half, Let's make it a random position from 0.252.75. So from quarter to three quarter of the way, from point C to point B. If we run this, we'll see some interesting results. Now this is already very cool because not every triangle is being cut by half. You can also go from 0 to one. So random points from 0 to one. So you'll go from one edge to the other edge. And this will create something even more random. But I like it at 0.252.75 because I don't want to cram things in one side more than the other. Small changes like that can create a completely different look. Let's finally randomize the division. So like we did with the square. So over here we already have inside of our recursive function, inside of our for-loop, show for each triangle this is going to happen. I want to create a different probability for the triangle to be cut then from its original state, from its previous generation. So all do if r is less than, let's make it half. So I'll make it 50 percent probable that this will be cut in half. And then for the else statement, I'll simply copy and paste whatever I had over here. And now I'll just do a, b, and c. So the original points, so whatever I copied over here for those three points, I'll plot in here. And now let's run this. We can run this several times to see how it looks. Maybe ten generations isn't enough. So we can add some more. Let's make it 15. Sometimes adding five generations deliberate too much, but let's try. And like the squares example, we can change the probability until we're satisfied to where the results. So you can make this 0.7. So you'll make it more probable that it'll be cut in half and that can allow us to return the number of generations or the resolution to attend. We don't need 15 for that. And we can run it as much as we want. And we can also save all of these results and choose one of them later. I'll do one last thing. I'm going to add another, a point or another P vector that I'm going to call a2. Because the other triangle that I'm going to create is going to share the BC sides with the first triangle. So this one is going to be a new p vector and it's going to be ads or a point at width and height. And then I'll just put it over here. So they both share points B and C. Now, if I run this, we have a canvas filled with triangles. So if I actually go back and make this, and by the way, we can delete all of this. We don't need it anymore. So if I make this one generation and then I run it, you'll see the two triangles, the initial triangles. And if I make it two generations, and I'll return it back to 10. And then we have the final result. And this is definitely more than enough to learn about recursion. I gave you three examples, one of which I've seen in a multitude of areas in different programming languages. But the other two using ArrayList. I've learned from Daniel shipments book the nature of coat. He does a different example. The book tackles couched line using ArrayLists and objects. And it's a great way for you to challenge yourself if you want to go and try that. But these are great examples to start with, to play around with recursions, and to learn how recursions actually work visually On to the next trapped her to check out how you can submit your own project.
8. Project: Now it's your turn to have fun and be creative. Choose one of the three approaches that we learned in this course to create your own recursive design. It doesn't have to be elaborate. Simple changes to the same designs we created in this course are fine as well, like using other shapes or a different coloring method. You can also challenge yourself and make one of the well-known examples that I mentioned earlier in the course, there will be some good practice for you. I can't wait to see your fabulous designs, so start making your recursive artworks now.