Moving the drawing¶
In the previous examples, we made several drawings composed of basic shapes. In doing so, it was necessary to determine the correct position for each of these shapes to fit all the pieces together. For some drawings, it was possible (and in some tasks required) that the coordinates of individual points be calculated based on the known coordinates of other points. This computation could have been done outside the program and then the calculated coordinates could just have been entered into the program. However, it is better to perform such calculations in the program itself, for several reasons:
We may not calculate the coordinates correctly in the first attempt. In such a situation, it is easier to modify the calculation instructions (i.e. the program) than to calculate everything manually from start.
When we create the drawing ourselves, it may be that after the first version of the program we may want to add something, for example on the left side of the drawing, but that we do not have enough space. In this case, the whole drawing should be moved to the right, so that the x coordinates of all points are increased by the same value. If we manually calculated the coordinates of the points, we need to calculate them all again. In a well-organized drawing program, it is sufficient to change one number to move entire drawing to the right. This process may need to be repeated several times until we are happy with the position of the drawn part, so trying it out is a lot easier when the program does the calculation instead of us.
If we want to draw the same drawing in multiple places in the window, the benefits of computing inside the program come to light again.
We will now systematise the coordinate computation a bit more and use it to move the drawn objects more easily. Before we get started, it would be a good idea to check the background and answer these questions:
- (50, 60)
- Try again.
- (50, 80)
- Try again.
- (40, 70)
- Correct!
- (60, 70)
- Try again.
- (40, 60)
- Try again.
Q-39: What are the coordinates of a point 10 pixels to the left of the point (50, 70)?
- (50, 60)
- Try again.
- (50, 80)
- Correct!
- (40, 70)
- Try again.
- (60, 70)
- Try again.
- (40, 60)
- Try again.
Q-40: What are the coordinates of a point 10 pixels below the point (50, 70)?
- pg.draw.rect(canvas, color, (70, 120, 50, 60))
- Try again.
- pg.draw.rect(canvas, color, (100, 150, 110, 120))
- Try again.
- pg.draw.rect(canvas, color, (100, 150, 50, 60))
- Try again.
- pg.draw.rect(canvas, color, (70, 120, 80, 90))
- Correct!
- pg.draw.rect(canvas, color, (70, 180, 80, 90))
- Try again.
Q-41: The rectangle is drawn using pg.draw.rect(canvas, color, (100, 150, 80, 90))
. How can one draw a rectangle of the same size, located 30 pixels to the left and 30 pixels above this rectangle?
- pg.draw.circle(canvas, color, (100, 120), 40)
- Correct!
- pg.draw.circle(canvas, color, (100, 160), 40)
- Try again.
- pg.draw.circle(canvas, color, (120, 100), 40)
- Try again.
- pg.draw.circle(canvas, color, (160, 100), 40)
- Try again.
- pg.draw.circle(canvas, color, (20, 120), 40)
- Try again.
Q-42: The circle is drawn using pg.draw.circle(canvas, color, (100, 200), 40)
. How can one draw a circle of the same size above this circle and touching it?
Changes to make a drawing easily movable¶
Let’s see how a cloud is drawn in the following example:
We presented the cloud with three circles, one larger in the middle and two smaller ones around it:
pg.draw.circle(canvas, pg.Color("white"), (200, 200), 50)
pg.draw.circle(canvas, pg.Color("white"), (150, 200), 30)
pg.draw.circle(canvas, pg.Color("white"), (250, 200), 30)
If we wanted to draw that cloud at different heights, we could repeat these three commands, each time with some new value for \(y\) coordinate of the centers of these three circles instead of 200, as it is in the first drawing. For example:
pg.draw.circle(canvas, pg.Color("white"), (200, 200), 50)
pg.draw.circle(canvas, pg.Color("white"), (150, 200), 30)
pg.draw.circle(canvas, pg.Color("white"), (250, 200), 30)
pg.draw.circle(canvas, pg.Color("white"), (200, 80), 50)
pg.draw.circle(canvas, pg.Color("white"), (150, 80), 30)
pg.draw.circle(canvas, pg.Color("white"), (250, 80), 30)
pg.draw.circle(canvas, pg.Color("white"), (200, 320), 50)
pg.draw.circle(canvas, pg.Color("white"), (150, 320), 30)
pg.draw.circle(canvas, pg.Color("white"), (250, 320), 30)
In this way, not only does the program grow faster than it has to, we also need to make every change in three places (for example, if we want to try 330 instead of 320, that change should be made in three places). Three changes are not many, but if we adopt this way of doing things, we would have more and more problems in more complex drawings, or in complex programs in general.
Instead, it is better to create a function and pass \(y\) coordinate of the centers as a parameter:
def cloud(yc):
pg.draw.circle(canvas, pg.Color("white"), (200, yc), 50)
pg.draw.circle(canvas, pg.Color("white"), (150, yc), 30)
pg.draw.circle(canvas, pg.Color("white"), (250, yc), 30)
cloud(200)
cloud(80)
cloud(320)
The new program is easier to read and modify further. For more clouds, or more complex clouds, the advantage of this approach would be even greater.
Now let’s consider how we should move the cloud to the left or to the right. We should increase or decrease the \(x\) coordinates of all circles (200, 150, 250) by the same value. For example, if we typed 260, 210, 310 as \(x\) coordinates, the entire cloud would be moved 60 pixels to the right.
It would be good if we could only use a single number to specify the horizontal position of the cloud. To achieve this, we note that the centers of the smaller circles are 50 pixels away from the center of the middle circle to the left and right. These distances do not change as the cloud moves. This means that if we denote \(x\) coordinate of the center of the middle circle with \(X_c\), then the centers of the smaller circles have \(x\) coordinates \(X_c - 50\) and \(X_c + 50\). Thanks to this relation (which does not depend on the position of the cloud), we can now also introduce the parameter \(x\) to the function that draws the cloud:
def cloud(xc, yc):
pg.draw.circle(canvas, pg.Color("white"), (xc, yc), 50)
pg.draw.circle(canvas, pg.Color("white"), (xc - 50, yc), 30)
pg.draw.circle(canvas, pg.Color("white"), (xc + 50, yc), 30)
cloud(200, 200)
cloud(200, 80)
cloud(200, 320)
Either of these three clouds could now be easily moved, for example, 60 pixels to the right, by typing 260 as the first parameter instead of 200 in the function calls. It is equally easy to make a drawing with several clouds. Color, or shade of gray, can also be a function parameter. This way, some clouds can be darker and some brighter.
When we use all of the above, we can create a program that draws several clouds of different shades, for example:
Let’s summarize, with small generalizations, what needs to be done in order to be able to show one drawing in various places:
We need to select one point whose coordinates are set directly. We call this selected point the main point, (sometimes this point is also called anchor). In the example of clouds, the main point is the center of the middle circle.
After selecting the main point, the coordinates of all other significant points are determined in relation to it by adding or subtracting a certain displacement to the coordinates of the main point. In the example with the cloud, to get \(x\) coordinate of the center of the left circle, from \(x\) coordinate of the main point (center of the middle circle) we subtract 50 pixels, and for the right circle we add 50 pixels.
In the general case, there may be shapes other than circles in the drawing. The significant points that determine the positions of these shapes are:
for a line: its ends
for a polygon: its points
for a circle: its center
for a rectangle: its upper left corner
for an ellipse: the upper left corner of the rectangle in which that ellipse is inscribed
All of these points should be given with respect to the main point, that is, their coordinates should be expressed as coordinates of the main point, increased or decreased by some value.
Check your understanding of the previous explanations and answer the questions.
- pg.draw.circle(canvas, pg.Color("red"), (x, y), 50, 1)
- Try again.
- pg.draw.circle(canvas, pg.Color("red"), (x+120, y+90), 50, 1)
- Try again.
- pg.draw.circle(canvas, pg.Color("red"), (x+20, y-10), 50, 1)
- Correct!
- pg.draw.circle(canvas, pg.Color("red"), (x-20, y+10), 50, 1)
- Try again.
Q-43: We want to customize a drawing consisting of several shapes so that everything is drawn relative to the anchor with the coordinates x = 100, y = 100. One of the statements that form a drawing is
What statement should replace the given one?
- pg.draw.line(canvas, pg.Color("red"), (x-50, y-50), (150, 150))
- Try again.
- pg.draw.line(canvas, pg.Color("red"), (x-50, y-50), (x+50, y+50))
- Correct!
- pg.draw.line(canvas, pg.Color("red"), (x-50, x+50), (y-50, y+50))
- Try again.
- pg.draw.line(canvas, pg.Color("red"), (x+50, y+50), (x+150, y+150))
- Try again.
Q-44: We want to customize a drawing consisting of several shapes so that everything is drawn relative to the anchor with the coordinates x = 100, y = 100. One of the statements that form a drawing is
What statement should replace the given one?
- pg.draw.rect(canvas, pg.Color("red"), (x-50, y-50, x, y))
- Try again.
- pg.draw.rect(canvas, pg.Color("red"), (x, y, 100, 100))
- Try again.
- pg.draw.rect(canvas, pg.Color("red"), (x+50, y+50, 100, 100))
- Try again.
- pg.draw.rect(canvas, pg.Color("red"), (x-50, y-50, 100, 100))
- Correct!
Q-45: We want to customize a drawing consisting of several shapes so that everything is drawn relative to the anchor with the coordinates x = 100, y = 100. One of the statements that form a drawing is
What statement should replace the given one?
- Instead of pg.draw.circle(canvas, color, (x, y), r, d) we call pg.draw.circle(canvas, color, (x+100, y), r, d).
- Correct!
- Instead of pg.draw.circle(canvas, color, (x, y), r, d) we call pg.draw.circle(canvas, color, (x-100, y-100), r, d).
- Try again.
- Instead of pg.draw.rect(canvas, color, (x, y, w, h), d) we call pg.draw.circle(canvas, color, (x+100, y, w+100, h), d).
- Try again.
- Instead of pg.draw.rect(canvas, color, (x, y, w, h), d) we call pg.draw.rect(canvas, color, (x+100, y, w, h), d).
- Correct!
- Instead of pg.draw.rect(canvas, color, (x, y, w, h), d) we call pg.draw.rect(canvas, color, (x-100, y, w, h), d).
- Try again.
Q-46: We want to move a drawing consisting of several shapes to the right by 100 pixels. Mark the correct claims.
The following are some examples of converting a fixed drawing to a movable one.
Teddy bear - position¶
The following program, which shows the toy bear’s head, is given:
The program calls the framed_circle function seven times, which draws the given circle with black border (though it could have been avoided for the three small black circles). To be able to change the position of the drawing, let’s select the main point (anchor). Make it the center of a large circle, that is, the heads of the bear. The coordinates of this point are (250, 150). Now we need to express the coordinates of the centers of all other circles relative to the main point. Take the bear’s right ear as an example.
\(x\) coordinate of the center of the right ear is \(310 = 250 + 60\), while \(y\) coordinate is \(80 = 150 - 70\). From here we can see that the coordinates of the center of the right ear can be written in the program as (cx + 60, cy - 70), where (cx, cy) are the coordinates of the main point.
Follow the same procedure for the other circles and complete the draw_teddy function.
This program allows us to easily display teddy bears in various places on the screen. For example, the function call
draw_teddy(width // 2, height // 2)
which draws a bear with the main point in the center of the window (as it was), can be replaced with the following two:
draw_teddy(width // 2 - 120, height // 2)
draw_teddy(width // 2 + 120, height // 2)
Try this! It would have been much more difficult to draw another bear if we had not adapted the initial program for this use.
House - position¶
Let’s say you wrote this program, and your goal is to remake the program so that the house can be easily moved:
Let the main point be: code: (x, y) = (50, 150). Complete the started remodeling of the program in the box below, where the drawing is done in the function draw_house(x, y, wall_color)
. After making sure that the drawings in the two programs look the same (except that they draw in windows of different sizes), replace the call draw_house(50, 150, pg.Color (" khaki "))
with the next 4, to get the picture as when clicking the “Play task” button:
draw_house(150, 90, pg.Color(220, 220, 220))
draw_house(220, 130, pg.Color("white"))
draw_house(350, 160, (255,255,150))
draw_house( 50, 150, pg.Color("khaki"))