Welcome to this new lesson. We are going to continue constructing our first behavior, which is the relationship that two objects will have with one another. And it's going to be a bouncing kind of behavior, right? So let's just jump directly where we left off into the code. So we're continuing from last lesson we wrote down, we want to bounce on the borders. And eventually, let's just add also here, in a way what we want to do today, which is bounce with each other, right? That's the function where we want to get. So let's start writing some of these functions. We have a setup that basically runs all the list of functions in the run function, right? Which is a function that for us works as an outline. And I really like this setup. I often kind of really start with the setup and I start really thinking on what are the behaviors that I really want to include in a class. And I just throw a few of them into the world and maybe start working on how to change their behavior, how to make them kind of be expressive, right? So let's just bounce on the borders. First, let's just write a function to bounce on the borders. Because if you remember, if we run this right now, the rectangles kind of reach the border, but they kind of keep going, right? So we could do a bounce. There's different ways of thinking of the edge condition. Let's just write the bounds which we've written before. But I think it's always good practice to rehearse a bit more bounce on borders. And we have. Here we go. So if our position is, Let's use the variables that we have actually, I always kind of of feel like I misspell a lot if I don't copy paste the variable. So if our position in x is smaller than 0, well, in that condition, we multiply the velocity by -1, right? So we invert the velocity self.velocity at mult -1. So that's one condition on x. The other condition, which we actually can copy is if this value of x is bigger than the size of the screen, right? And if you think about it, well, the size of the screen is defined by this variable, which is 1200. The fact that it's a variable is that this could change. We could use this 1200 directly, but this would be hard coding a value that is not related to the class or to the canvas, right? So we might want to, at this point, allow the object. And this is kind of a decision, an arbitrary decision that might be useful here is to think, well, how can this class know the size of the screen, right? Perhaps we can include a variable called something like x max. What is the maximum boundary on x, right? So let's do that x_max. If we need it, we could include a y max, but at this point we don't really need it. So I don't want to make this class longer of what it really needs to be, but we are going to need this x_max position or variable equals to x_max, right? And in this way we could actually pass information from outside the class that will tell the class this is how big the world will be, right? I would be curious to also change the zero which we've hardcoded here to be the minimum. In a way you could imagine doing a x minimum class. So what is the kind of the lower boundary we are assuming here is zero. But if we would want this class to be a little bit more flexible and be able to bounce on more arbitrary boundaries, we might want to include an x minimum as well. But I'm just going to do this for now. So I'm pointing it out. As you go through the documentation, you will be able to see how the minimum version could also work. So let's, if we would try to run this class now, the fact that we have this new max value which we will be using here. If the class, if the position in x is bigger than the maximum boundary bounds, right? But if we try to run this, the class will fail because we're not providing the right number of arguments. So we have three arguments, vector, integer vector. We need a fourth one, which will be the image sizing x, which is the upper boundary. So how big the screen is will determine what we're considering the maximum size of the screen, right? So let's try that. Let's just make sure that now that we have this function, we are effectively using it, because if we don't use it, we don't call it into our run, it's not going to be executed, right? There we go. So we have the run function is being executed and we are going to go through three behaviors. We are running into an error. Let's just try to find the issue. Okay, I think I see the issue. I keep forgetting ourself is a variable that belongs to the class. There we go, so now we could see that our rectangles remain within the canvas as they're bouncing in the borders, right? So let's just do the, The bounce with color, which is again registering movement, registering motion, right? And color, we currently are not using color at all. This is something we can do relatively quickly. I think let's just construct a few variables all the way up here. And these are not necessarily variables that we need to, To specify as part of the constructor. These are going to be internal variables, rgb. R change. And here we could say this is going to be a 0.1, g change, it's going to be value of 4, and b change is going to be a value of 1, right? So we have some values that are going to be used for color, and now we can actually make the function bounce color. And if you remember correctly, what we're doing here is that the colors, each one of these rectangles is going to have its own color. But this color is going to be changing and it's going to be bouncing in the maximum number of each one of the channels. The red channel goes from 0 to 255. So at 255 is going to go back to 0 and at 0 it's going to go back to 255. And each one of the channels is going to be bouncing independently, therefore creating kind of a gradient effect, again, an effect that we've covered already. So let's do plus equals self.rchange. Right, I believe that's the way we called rchange, gchange, bchange, right? So these are the three channels, r, g, b. So we want to do r, g, b and r, g, and b, right, and that is just changing. These values are going to tend towards white as we go from 0 to 255. If we actually, hit a condition like if the red value is smaller than 0, then we want to do self.rchange times equals minus 1, right? So this condition is flipping the rate of change for the red value when it reaches its lower boundary and its upper boundary, right? The same thing happens, it basically flips. So make sure that the column here is a little bit more visible. So that's just for the red, and we would repeat this equation for the green and the blue. So let's just make sure we do green and blue. And here the same thing, let's just do red, green and finally blue, right? So we finally, oops, that's the way you define the function, right? So we have self.bounce color, right? If you run this at this point, again, nothing's going to happen because we are not using the r, g, b colors anywhere. In the script, we need to make sure that once we fill this rectangle, we use r, g, b, right, or, sorry, self.g. Right, so we're actually using the internal variables that we've calculated and they're kind of bouncing around to control the color. So let's see if this is working. So you see, it is in fact working, but they're all kind of palpitating between these colors synchronously, or like at the same time with the same starting color. If you would want to randomize some of those colors, you can do so as well. We could actually, make any of these r, g, b attributes being something that you randomize, right? But I think that's maybe something that it's more in your hands of exploring like, I invite you to turn off this background and see how this kind of gradient actually paints into the screen. You might also at this point want to remove from the display function, right, or the display method. Let's just go back to no stroke because the gradient effect doesn't really work if it has a stroke that we kind of introduce at some point. As you can see here, we have these gradients, and the gradients are being shifted based on the speed of movement, right? So that's the effect that we're going for. But that effect, I'm going to go back to leaving the background painted because I want to see a bit more clearly the behavior of the rectangles, right? Mainly because we're at the point where we really need to write the final function, which is the bouncing with each other, right? Each rectangle, we will do so that let's just run it with maybe a few rectangles and explain what we are trying to achieve. So these rectangles, at a certain point, they are aligning with each other, or they get to a point in which arguably, they could bounce with one another out of their relationship of the upper and lower entity in the list, all right? So what we really, want to understand is how these rectangles would actually register their neighbors, these neighbors in this list, and actually change their behavior, right? We know that the behavior of bouncing is basically flipping the velocity. So we know how to do the bounce, but we haven't seen how to talk. How would one rectangle as an object talk to its adjacent or its neighbor in a list, right? So let's look into that. So the first thing I would like to do is understand that all these rectangles belong to a list, right? So the function that we would call bounce with each other, right, which we could start writing somewhere here. Right, bounce with each other needs to take in consideration a who am I as a rectangle, which is my own index in the list. And do I have access to the list? Can I read the list and determine who would be adjacent to the left or up in a way or adjacent below me, right? Remember that lists, it's a data structure that it's an ordered data structure that we can rely on having an entity before or ahead of us, and that being always the same entity, right? So if we have been sequential on the construction of that list, we can in fact ask the question, who is the next entity in the list? And that will tell me, is the object below me, right? Granted, there's a few exceptions. If the object that we're asking for, we're the first object in the list and we're asking for an entity before us. There is not going to be an entity before us, and if we are the last object in the list, we might not be able to ask for the last one, right? So this function will need to take in consideration that there are two exceptions, at the beginning, at the end of that list, right? But this class at this stage doesn't have any information of how to access. Let's just ask here. Access the list. And also access my index in the list. So who am I in the list as an index? And what is the list? Those are the two pieces of information that we currently don't have. So let's just include those into the constructor. Assuming that one we are going to call the index, which is going to be a variable, an integer that will represent our index in the list. And then let's call the other list, right. Let's just do the non capital underscore, right. So index is going to be an integer and this is going to be a list, right. So once we provide those information outside the class, we need to keep a reminder of what we are intending those variables to be, right. So let's just be self.index = index and self., others list. I'm calling it the others list because I like thinking conceptually that when we're writing from within the class, right. We have to think of how this entity would relate to other entities, right? So the list of other entities, in this case it's represented by the others list. So we have two new entities or two new arguments in the constructor. Let's go into our constructor. This is where I really, when this constructor starts getting long, this is where I really like separating them in line by line. I know it gets a bit longer, but I think that it kind of helps clarity, right. And it also helps to remember this kind of optional declaration of what each entity is, right. I'm going to do that here just for the sake of clarity for you, that you know which piece of information are we providing in each case, right. That's what we have so far. Now we need to provide the index, and the list of others. So we don't have this index yet and the list of others, we don't have it yet. Right, so what would be the index? The index will be this i, right. I is as we're constructing the objects in a for loop and we are appending them to the list, the I represents the index of the current entity we're creating. So we will let the entity know our kind of gradient rectangle know that this is your index, right. If you're the 0 index, you're going to be the first entity in the list, right? And then the list is the actual list that we're constructing. So if we pass in the list into the class, the class can do list calculations, calculations about who would be my adjacent neighbors in the list or kind of other entities in the list. Right, so this is the list variable that we constructed. We're passing it into the class now, for that class to be able to perform operations with that, I think we're good here. Now we can go back to the class and having the two pieces of information that we discussed, we can actually write this function, right? So let's just create an exception, let's start with an exception, right? What is the first exception? If the self.index > 0, right? And this is a way of saying, let's just start this calculation of bouncing for entities that are the number one entity in the list, knowing that the entity 0 will behave differently because it doesn't have an upper limit, right? So let's just grab one entity in the list right? So other_up which will be the name we'll give to an entity that is above us, right? So let's do self.others, how did we call the list? We called it others_list, right. Let's just make sure we spell that, so self.others_list. And here, as in any list, we can provide the index. So we know that the index value, the self.index is our index, but the self.index-1 means the entity before me, right? Right, so this we can make a comment. This is the other up, right? So that represents the entity that would be above me. And let's calculate the distance. So this is going to be a variable that is going to be, let's calculate the distance between the position in x minus the other's position in x. So we're only concerned about position in x in this condition, right? So the distance, we can do it as a simple subtraction of. So let's do self.vec_position.x- the other_up, and the other_up. This is interesting because this is an instance of the class. So now we can actually, the other_up has its own version of this vector position variable. So we can, we haven't seen this before. We have an instance of a class, and now we're accessing its internal data, which is the vector position of that other entity and the x, right? So the distance will be a calculation. And let's just do this calculation depending on if we're left or right of this entity, could be positive or negative. So let's make it an absolute value value of this calculation, right? So we're aiming for a value that is very small, probably less than one, less than two. So proximity is something that we could say, kind of, it should be zero, but we want to give it a bit of tolerance for this, right. So we're going to say if the distance is smaller than 2, what needs to happen? Well, we can do what we do in the bounds, right? Let's just look at the bounds. The self.multiply time- 1, basically that should be enough. We could do a few other things, like changing the color just to kind of signify that this something has indeed happened. But let's just start here by executing this function. So here we go, let's make sure that we have everything in the outline. Our main class hasn't changed, let's see if we're running this well. Okay, so we're seeing, in fact, we don't see any bouncing happening yet. So it might be that we're not running any errors, but we are not getting the behavior that we're expecting. Let's just double check. So one of the things that we can consider, is that we also want to change the velocity vector of the other, right. So, at a time in which we are kind of aligning with each other, we get 0. Flip the velocity vector -1 to both us and our neighbor. So you can see here now we are starting to see the desired behavior, right. As you can see, when two entities are getting close to one another, they do start kind of bouncing. And this takes us to an interesting point. While the setup was all deterministic, there's kind of a clear kind of simple behaviors that are kind of building up the final behavior or the final kind of composition is quite difficult to predict, right? Starting to become more interesting from the perspective of emergence, right? From the perspective of behaviors that, that need to be simulated in a way to be understood or executed. So let's just make it a little bit more visible for us to see here. Self.r = 0, so we're going to reset the red value on collision self.green, it's going to be 255. And self.b = 0, right. This is going to make that the green, we're going to have a green blip if you want. Whenever you see how a collision happens, we actually mark that with color, right? And if we want to make that more evident, I think that it would be interesting that we don't start. Let's start with a value of 150 here, maybe 80 here. So that the color patterns that we going to go through are slightly different and you see how we're registering at this point. The behavior is only happening from on my above parent, right. It's not technically a parent, but it's a neighbor that actually we're calculating above. We don't really need to do this behavior in both directions. You could, but I invite you to really play with the different behaviors that you can actually obtain by being able to change your own movement based on your neighbor's position. This is kind of the, this whole script has been to understand this line here, which is the other, right. How do we actually access the list? And through our own understanding of the index that we as a class have in that list, we can understand the index of the other entity which is in this case an entity, just one before us, right? So above us, right, and then do some calculations in relation to that. So we have a very foundation of a behavior where two classes start interacting with one another. Just to conclude, maybe, and this is just the beauty of writing these things in a way that it's associative. We can actually turn off the background so you can actually see them in their kind of on the manifestation as a gradient, right. And you can see some of them are kind of bouncing here, a bit stuck here. But this is kind of the emergence of a composition in a way that is fully procedural. It's actually emerging out of the behavior of these rectangles and we can actually also increase the number of entities, right. So I invite you to think of how these compositions that are written in a behavioral manner are the beginnings of a design practice that is using code and it's using algorithms such as these ones to construct different graphic templates, different forms of architecture. We're starting to deepen, these principles apply in different dimensions. So we're going to leave this video here and I'm going to continue on this week in the following one. I'll see you then.