Page 1 of 1

Python with Timmy the Turtle 2

#1 andrewsw  Icon User is offline

  • Fire giant boob nipple gun!
  • member icon

Reputation: 3253
  • View blog
  • Posts: 10,908
  • Joined: 12-December 12

Posted 29 September 2013 - 03:29 PM

The first part is here

Python docs - Turtle Graphics

  • Optional Arguments (for Functions)
  • If Statement
  • Equilateral Triangle
  • Rectangle
  • Modulo Operator
  • Circles
  • Regular Polygons (Circumscribed)
  • Regular Polygons
  • Repeating Shapes
  • Stars


Optional Arguments (for Functions)

At the end of the first part we had a function which accepted a turtle and side as arguments in order to create a square:

def square(aturtle, side):

I want to extend this so that we can also supply a colour to fill in the square:

def square(aturtle, side, colour):

I'm using the UK spelling of colour as it will never conflict with any existing color property (that I have encountered).

This is fine, but I won't always want to fill the square. Let's make it an optional argument:

def square(aturtle, side, colour = None):

The equals-sign makes it optional and None is the value it will assume if the colour isn't supplied when the function is called. We could have used colour='red' so that it will default to a fillcolor of 'red', but if I omit the colour I want the square not to be filled with any colour.

If Statement

In the function itself (the function-body) we need to use an if statement to check whether a value for the colour was supplied:

if colour is not None:
    # do something

This says 'if the colour is anything other than None'. The colon : at the end of this statement is required.

The if-statement is a statement-block, so the code that should apply if the condition is true (that is, if a colour was given) needs to be indented. As mentioned in part one, you can use tabs or spaces to create the indent, as long as the same indent-level is maintained. DO NOT switch between tabs and spaces: choose one and stick to it.

Spoiler

There are two if-statements. One to decide if we need to use begin_fill(), and the other to decide if end_fill() is needed. Notice that it is the same test condition for both statements. (Note that this is not the only way to write this code - there rarely is just one way!)

Equilateral Triangle

As drawing an equilateral triangle (with equal sides) is very similar to drawing a square, here is a simple function to do this:

Spoiler

However, see the sections on Regular Polygons which provides other ways to draw these shapes.

Rectangle

We should be able to create a version of our 'square' function that would create a rectangle. Mathematically, a rectangle is distinguished by having opposite sides of the same length. What does this mean to our turtle?

  • He draws one line and turns left 90 degrees.
  • He draws a second line (of a different length) and turns left 90 degrees.
  • He draws a third line, but it is the same length as the first, and turns again.
  • He draws a fourth line, but it is the same length as the second.


In our current function, x assumes the values 0, 1, 2, 3 each time through the loop (during each iteration). We need to distinguish, and handle differently, the values 0, 2 and 1, 3.

Modulo Operator

the docs said:

The % (modulo) operator yields the remainder from the division of the first argument by the second.


The Docs - Expressions
This link is broken: use the following, removing the spaces:
http:/ /docs.python.org/3.3/reference/ expr essions.html

Andy said:

0 % 2 gives us 0 (the remainder after division)
1 % 2 gives us 1
2 % 2 gives us 0
3 % 2 gives us 1
(4 % 2 would give 0, etc.)


You might also want to investigate Modular arithmetic. For example, if we count modulo 6 we would obtain 0,1,2,3,4,5,0,1,2,3.. The number 6 never appears in the output and the values "wrap around".

Spoiler

Notice that == is used to test for equality, != would test for inequality. See the Comparisons section in the linked Expressions page above.

Circles

We could easily draw a circle using turtle.circle(radius) but that is not very challenging, or educational.

Imagine the turtle has moved along a horizontal radial line, and then turns left 90 degrees. We want to draw a short line that follows the path of the circle. The turtle only draws lines, not curves, so it won't follow the exact path of the circle, but if the drawn-lines are short enough then we will, eventually, obtain our circle.

If the lines were infinitely short, and we drew infinitely many of them, then it would eventually draw a perfect circle. (This is actually one way of defining a circle.) But we don't have the time, or the pixel-fractions, to achieve this!

The circumference of the circle = 2 * pi * radius. This is the total distance we need to cover. A circle completes 360 degrees, so the total of all the left() values we use must add up to this figure. If we want to use 100 small lines to outline our circle then we can just divide both the circumference and 360 (degrees) by 100, and use these values as the forward() and left() values for our turtle.

Our circle won't be very accurate unless we use a large number of divisions (small lines), but if we use too many lines then the drawing will be slow.

Spoiler


Regular Polygons (Circumscribed)

In fact, all regular polygons (equilateral triangle, square, pentagon, hexagon) can be drawn in this way. That is, by drawing lines (of the same length) within a circle: the shape's circumscribed circle.

Spoiler


Posted Image

In order to retrieve the first colour ('red') from our colours List we need to use colours[0]. Because the shape variable starts at the number 3 we need to use colours[shape - 3].

Regular Polygons

The previous section drew regular polygons using their circumscribed circle. We divided both the circumference, and 360 (degrees) by the number of sides of the shape. We are making this more difficult than it needs to be!

Basically, it doesn't matter what the length of the sides are. As long as they are the same and we use left() to complete a 360 degree turn, we will have drawn a regular polygon. Consequently we don't need to divide, or even work out, what the circumference is. (I'll leave you to research the geometry!)

Also, by taking this simpler approach, we can supply the length of the side for our shape, rather than the radius that will contain it. The length of the side is a much more useful, and meaningful, value to us than the radius.

Spoiler

We can easily modify this to add stamps half-way along each line. Instead of moving forward(length) we can move forward half the length, stamp, then complete the second half of the line.

Spoiler


Repeating Shapes

Turtle graphics get really interesting when we can repeat creating shapes in a loop. We could create a circle, move the turtle, create another circle, etc.. Very intricate patterns can be created with a little bit of effort and planning!

We can use most of the functions that we have so far to achieve this. We just create a loop (a for or while loop) and call one of the functions within the loop. Within the loop we might move the turtle each time, or call the function with different arguments, or both.

Spoiler


Posted Image

Can you work out why timmy is moved forward 5 each time?
What happens if you reverse the loop using range(4, 10)?

Admission: Another way to draw regular polygons is:

timmy.circle(100, None, 4)

where 4 is a number of steps. (There are other ways.) However, we would not have learnt as much!

Stars

I'll admit that I modified the following example from something I found on the internet. I encourage you to investigate the geometry of a five-pointed star or pentagram.




The following may, or may not, help you with the geometry: I've never used Microsoft Visio before!

Posted Image
notes
Spoiler




the code
Spoiler


Posted Image

We have already seen most of the code that is used in this example but there are a few new features for you to investigate:

  • It uses the random module to generate random numbers.
  • Three of the functions return a value, which is stored in a variable by the calling code.
  • One of these functions returns a tuple - a grouping of values (2 values in this example).
  • numinput() prompts for a number from the user. There is also textinput() that you can use. These were added in Python 3.0. If you are using an earlier version then use input() (or raw_input()) instead to obtain a value from the console.
  • The function int() is used to convert a number (which might contain decimals) to an integer.


Are the three functions random_color() length() and coords() really necessary? They don't do very much, particularly length().
length() and coords() might be more useful if you could supply the range of values as arguments to these functions. Explore, and enjoy!

In Concluding

I haven't covered every feature, or method, of turtle graphics, so I'll repeat the link to the docs:

Python docs - Turtle Graphics

python turtle demos

This post has been edited by andrewsw: 04 October 2013 - 01:29 PM


Is This A Good Question/Topic? 1
  • +

Replies To: Python with Timmy the Turtle 2

#2 fatihmert  Icon User is offline

  • New D.I.C Head

Reputation: -1
  • View blog
  • Posts: 43
  • Joined: 04-March 12

Posted 15 July 2014 - 11:10 PM

Hello,
I was writing fractals, Here my example,

# -*- coding: utf-8 -*-
import turtle

class Lindenmayer(turtle.Turtle):
    "Bracketed L System"

    syms = {
        "F":"gforward",
        "f":"jforward",
        "+":"tright",
        "-":"tleft",
        "A":"gforward",
        "B":"gforward",
        "U":"penup",
        "D":"pendown",
        "[":"push_stack",
        "]":"pop_stack",
        "1":"color1", # black by default
        "2":"color2", # red by default
        "3":"color3", # green by default
        "4":"color4", # blue by default
    }

    def draw(self):

        self.hideturtle() # to speed up the drawing
        for x in self.string:
            try:
                getattr(self,Lindenmayer.syms[x])()
            except KeyError:
                "For actions that don't do anything"
                pass

    def gforward(self): self.forward(self.d)

    def jforward(self):
        "Go forward, but don't draw"
        self.penup()
        self.forward(self.d)
        self.pendown()

    def tleft(self):
        "Turn left by degrees"
        self.left(self.a)

    def tright(self):
        "Turn right by degrees"
        self.right(self.a)

    def push_stack(self):
        "push state to stack"
        self._stack.append((self.pos(), self.heading()))

    def pop_stack(self):
        "pop and restore state"

        pos, heading = self._stack.pop()
        self.penup()
        self.setpos(pos)
        self.setheading(heading)
        self.pendown()

    def color1(self):
        self.pencolor("#000000")

    def color2(self):
        self.pencolor("#ff0000")

    def color3(self):
        self.pencolor("#00ff00")

    def color4(self):
        self.pencolor("#0000ff")

    def fix_rules(self):
        "Add unspecified conversions. They stay the same"

        parts = set("".join(self.rules.keys() + self.rules.values()))

        for x in parts:
            if not x in self.rules.keys():
                self.rules[x] = x

    def init(self, depth, speed=0):
        "Prepare required variables before draw"

        self._stack = []
        self.speed(speed)

        self.depth = depth

        string = self.begin

        self.fix_rules()

        for _ in xrange(depth):

            string = "".join(self.rules[x] for x in string)

        self.string = string

        self.after_init()

    def after_init(self):
        "Extra stuff, executed right after init function"
        pass

#### Begin L-System Equations ###

class koch(Lindenmayer):

    rules = {'F':'F-F++F-F'}
    begin = "F"
    a = 60

    def after_init(self): self.d = max(400 / 3 ** self.depth,1) # set distance appopropiate to complexity


class square_koch(Lindenmayer):

    rules = {'F':'F-F+F+F-F'}
    begin = "F"
    a = 90
    def after_init(self): self.d = max(400 / 3 ** self.depth,1)


class sierpinsky(Lindenmayer):

    rules = {'A':'B-A-B', 'B':'A+B+A'}
    begin = 'A'
    a = 60

    def after_init(self): self.d = max( 300 / 2 ** self.depth,1)

class fibonacci_tree(Lindenmayer):

    rules = {'A':'AA', 'B': 'A[-B]+B'}
    begin = "B"
    a = 30

    def after_init(self): self.d =  max(300 / 2**self.depth,1); self.left(90)

class dragon_curve(Lindenmayer):

    rules = {'x':'x+yF', 'y': 'Fx-y'}
    begin = "Fx"
    a = 90

    def after_init(self): self.d =max( 300 / 1.5 ** self.depth, 1)



class fractal_plant(Lindenmayer):

    rules = {'X':'F-[[X]+X]+F[+FX]-X', 'F': 'FF'}
    begin = "X"
    a = 25

    def after_init(self):
        self.d = max(120 / 1.9 ** self.depth, 1)
        self.left(90)
        self.pensize(3)

if __name__ == "__main__":

    from time import sleep
    wn = turtle.Screen()

    things_to_draw = (
        (koch,3),
        (square_koch,3),
        (sierpinsky,6),
        (fibonacci_tree,5),
        (dragon_curve,10),
        (fractal_plant,5),   
    )

    for c, x in things_to_draw:
        wn.clearscreen()
        f = c()
        f.init(x)
        f.draw()
        sleep(1)

    wn.exitonclick()


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1