Drawing with the help of loops

Consider the following task: let’s draw 6 circles, as in this picture:

../_images/target.png

Looking at the picture, we can assume (or it could have been said in the problem setting) that the circles are equally spaced. This means that the radius difference of each two adjacent circles is the same.

We choose the size of the circles to be as large as possible, but to fit in a given drawing space of 300x300 pixels. Since the width of the window is 300 pixels, the radius of the largest circle is 150. For the difference of radii of the two adjacent circles we can take \({150 \over 6} = 25\). This gives radii of 25, 50, 75, 100, 125, 150.

Based on the calculated values, we could write a program like this:

Let’s imagine that after this we were given the new task of making the same drawing, but with 5 circles. This is a very small change, isn’t it? We should be able to reuse the previously solved task.

When we start working on a 5-circle drawing, we see that very little of the previous program can be used. In fact, we can only use the idea, and the size of the circles should be calculated from scratch.

If we had written the program differently, customizing it would have been much easier. We could, for example, write the number of circles in a variable and then use that variable in all the necessary calculations. This program would look like this:

In this program it is enough to change only one number so that it draws any given number of circles.

As we said earlier, many drawings have some regularity, such as symmetry or some repeating part (and many other, more complex patterns). If we understand the regularity in such drawings and express it mathematically, we will be able to use it when writing a program to draw such drawings, as we did in the previous example. That way we get a program that is much easier to modify to get another, similar drawing. For drawings with a large number of repetitions of a part (identical or slightly modified), the program that uses regularity will also be much shorter.

Many programs used by millions of people are constantly being improved and refined and new versions of such programs are being published. Therefore, program changes are something completely normal that happens all the time. The situation is similar with the programs we write ourselves. When we write a program, it can easily happen that we later think of something new and want to modify a part of the program that has already been written.

Therefore, when writing programs, we should keep in mind that someone (possibly ourselves) will want to create a similar program and may want to use our program as an initial version.

Let’s look at another example of how we can use the regularities in a drawing to write a more flexible program (a program that is easier to adapt to a slightly different purpose).

Example - antenna

We have already seen a program drawing this antenna. Now the program is written so that it is not too difficult to change the number of transverse segments, the spacing between them, the difference of lengths of successive segments and the like.

Part of the program that draws the transverse segments of the antenna could be written as follows:

for i in range(6):
    pg.draw.line(canvas, pg.Color('darkgray'), (120 - 10 * i,  75 + 25 * i), (180 +  10 * i,  75 + 25 * i), 1 + i//2)

The program written in this way would be slightly shorter, but the first one is clearer, so each has its advantages. Let’s just point out that both of these programs are better than drawing 6 lines one by one for transverse segments (we used to do). If this part of the program consisted of six calls to the line drawing function, it would be more difficult to modify and adjust the program to draw a different antenna.

Equidistant numbers

In both previous examples, it was necessary to enumerate one or more series of equidistant numbers. In the task with circles, these were numbers 25, 50, 75, 100, 125, 150 (radii of circles), and in the task with the antenna, we needed as many as four series of numbers - x and y coordinates of the ends of the transverse antenna segments. In particular, these numbers are:

  • x coordinates of left ends: 120, 110, 100, 90, 80, 70

  • y coordinates of left ends: 75, 100, 125, 150, 175, 200

  • x coordinates of right ends: 180, 190, 200, 210, 220, 230

  • y coordinates of right ends: 75, 100, 125, 150, 175, 200

We have seen that there are different ways to get the values we need. For example, in a task with concentric circles, values 25, 50, 75, 100, 125, 150 we could obtain in any of the following (equally good) ways:

for r in range(25, 151, 25):
    pg.draw.circle(canvas, pg.Color("red"), center, r, 2)
for i in range(br_krugova):
    pg.draw.circle(canvas, pg.Color("red"), center, round(25 + i * 25), 2)
r = 25
for _ in range(br_krugova):
    pg.draw.circle(canvas, pg.Color("red"), center, r, 2)
    r += 25

In the general case, if we need to get a series of values of a, a+d, a+2d, … a+(n-1)d, the previous three methods can be used as follows:

for x in range(a, a + n*d, d):
    print(x)
for i in range(n):
    print(a+i*d)
x = a
for _ in range(n):
    print(x)
    x += d

We will see that many tasks with drawing equidistant shapes can be solved by applying loops like this.

Note that the range function with a step (with three arguments) must receive integer arguments, so in situations where the step is not an integer its use is not possible.

When we need (as in an antenna assignment) to make several series in one loop, the first mode is less convenient, so we have to choose one of the other two ways.

The following questions will help you consolidate your knowledge of forming a series of equidistant numbers.

    Match a series of numbers with a loop that generates it. try again!
  • 100, 200, 300, 400, 500
  • for i in range(100, 600, 100)
  • 100, 300, 500
  • for i in range(100, 601, 200)
  • 100, 200, 300, 400, 500, 600
  • for i in range(100, 601, 100)
  • 200, 300, 400, 500, 600
  • for i in range(200, 601, 100)
    Match the numbers obtained with the expression in the "for i in range (5):" loop that generates them. try again!
  • 100, 150, 200, 250, 300
  • x = 100 + i*50
  • 50, 150, 250, 350, 450
  • x = 50 + i*100
  • 0, 100, 200, 300, 400
  • x = i*100
  • 100, 200, 300, 400, 500
  • x = 100+i*100

    Q-47: Which expression should be used in the loop

    for i in range(19):
        x = ???
        ...
    

    for x to have the same values as in a loop

    for x in range(25, 500, 50):
        ...
    
  • x = 25 * i + 50
  • No.
  • x = (25 + i) * 50
  • No.
  • x = 25 * 2*i+1
  • No.
  • x = 25 + 50 * i
  • Correct!

The following are the tasks for the exercise.

Ladder

Modify the program so that the ladder steps are drawn in a loop.

Instead of 5 line drawing statements, you can use a loop of the following form:

for y in ???:
    pg.draw.line(canvas, pg.Color("brown"), (100, y), (200, y), 10)

To complete the loop correctly, you need to answer the following question:

    Q-48: Which of the ranges offered gives values 50, 100, 150, 200, 250?

  • range(0, 50, 250)
  • No, the first number is not appropriate for that range.
  • range(250, 50)
  • No, try again.
  • range(50, 251, 50)
  • Correct!
  • range(50, 250, 50)
  • No, the last number is not appropriate for that range.

Trees

Modify the program so that one tree is drawn in each or the three passes through the loop.

The program can look like this:

whereby appropriate expressions for the x coordinate should be placed instead of the question marks. When i takes the values 0, 1, 2 in order, the expression in the first statement should take the values 40, 140, 240 and the expression in the second statement should take the values 10, 110, 210.

Grid

Modify the program so that vertical lines are drawn in one loop and horizontal lines in the second loop.