How to make unfilled polies without retracing path in turtle? - python

I am working on a turtle project where the user can draw their own avatar.
The thing is, I want the user to be able to decide whether the turtle they drew will be filled, or not.
The filled part is simple, as, by default, the polies will get filled,
but the only way I know how to avoid filling is to retrace the lines so that the end of the line meets the start of the line.
Is there a built-in method or a more efficient way to keep the polies empty?
The problem with my current method is that the more lines (parameter) the lines there are for each turtle, the more stucky the program runs (the area doesn't influence the smoothness of the program).
import turtle
wn = turtle.Screen()
pen = turtle.Turtle('circle')
pen.shapesize(0.1, 0.1)
cor = []
# function to draw with the pen
def drag(x, y):
wn.tracer(0)
pen.goto(x, y)
cor.append(pen.pos())
# function to break out of while loop
def fill():
global done
done = True
# function to break out of while loop and set fill to False
def nofill():
global done, fill
done = True
fill = False
wn.listen()
wn.onkeypress(fill, 'f')
wn.onkeypress(nofill, 'n')
pen.ondrag(drag)
done = False
fill = True
while not done:
wn.update()
pen.begin_poly()
if fill:
for c in cor:
pen.goto(c)
else:
for c in cor[::-1]: # first go backards, then forward to avoid fill
pen.goto(c)
for c in cor:
pen.goto(c)
pen.end_poly()
wn.register_shape("mypen", pen.get_poly())
wn.clear()
example = turtle.Turtle('mypen')
Example with fill:
Run the above code.
Draw a shape.
Press f
Example without fill:
Run the above code.
Draw a shape.
Press n

Is there a built-in method or a more efficient way to keep the
polies empty?
I'm not aware of any functionality to avoid closed polygons on turtle cursors -- your solution is clever! Below is my rework of your code to simplify this approach:
from turtle import Screen, Turtle
def drag(x, y):
''' function to draw with the pen '''
turtle.goto(x, y)
done = False
filled = True
def fill():
''' function to break out of while loop '''
global done
done = True
def nofill():
''' function to break out of while loop and set fill to False '''
global done, filled
done = True
filled = False
screen = Screen()
screen.tracer(False)
screen.onkeypress(fill, 'f')
screen.onkeypress(nofill, 'n')
screen.listen()
turtle = Turtle('circle')
turtle.shapesize(0.1)
turtle.ondrag(drag)
turtle.begin_poly()
while not done:
screen.update()
turtle.end_poly()
screen.clear()
polygon = turtle.get_poly()
if not filled:
polygon = (*polygon, *polygon[::-1])
screen.register_shape("mypen", polygon)
example = Turtle('mypen')
screen.tracer(True)
screen.mainloop()
The problem with my current method is that the more lines
(parameter) the lines there are for each turtle, the more stucky
the program runs
Unfortunately, the above code doesn't reduce the extra/excessive lines problem nor the general stuckiness of it (whatever stucky means, #Nick.)

Related

Trying to find the screensize and make an object go to the screensize over 2 with turtle

So I'm writing a tron game in python turtle and I want to make the two bikes start at whatever the screen width is/2, and height/2. (Same thing for the other but negative width). Sadly this doesn't seem to work because you can't divide a function by an int. Does anyone know how to do it?
This is what I tried:
width = turtle.window_width
height = turtle.window_height
def tron():
#Drawing the starting turtles
blueplayer = turtle.Turtle()
redplayer = turtle.Turtle()
screen = turtle.Screen()
screen.setup(width, height)
screen.bgpic('TronBg.png')
screen.bgcolor('black')
screen.addshape('BlueBike.gif')
screen.addshape('RedBike.gif')
blueplayer.shape('BlueBike.gif')
redplayer.shape('RedBike.gif')
redplayer.pencolor("red")
redplayer.pensize(3)
blueplayer.pencolor("blue")
blueplayer.pensize(3)
redplayer.pu()
blueplayer.pu()
-> blueplayer.goto(width/2, height/2)
-> redplayer.goto(-1*(width)/2, height/2)**
redplayer.pd()
blueplayer.pd()
#Box border
#Border
box = Turtle()
box.ht()
box.color('purple')
box.speed('fastest')
box.pensize(10)
box.pu()
box.setpos(-355, -345)
box.pd()
for i in range(5):
box.forward(700)
box.left(90)
tron()
Change your code as follows and the problem will go away:
import turtle
width = turtle.window_width()
height = turtle.window_height()
P.S. to obtain window width you call in case of the turtle module a function. To call a function it is necessary to provide () after the function name, else width will become just another name for turtle.window_width function.
If you don't put the brackets after turtle.window_width in width = turtle.window_width you can solve the problem by adding width = width() in the next line. Try this out to see that it works in order to gain better understanding of what an assignment operator = does.
By the way: you can check which type and which value the variable width has with print(type(width), width). Such print debugging is often helpful because sometimes it is necessary to use value = module.some_name and sometimes value = module.some_name() to get the right value.

How to get position of turtles ? (turtle module python)

I'm using turtle module and I want to get the position of turtles. I looked at the documentation, but maybe I just don't see it.
I would like to know if two turtles are at the same position, so a code like this :
from turtle import *
turtle1 = Turtle()
turtle2 = Turtle()
pos1 = ... #Should be turtle1 position
pos2 = ... #SHould be turtle2 position
if pos1 == pos2:
#do stuff
Since turtles crawl a floating point plane, an == (equal) comparison won't work well in the long run. You want to get the distance between two turtles and decide how close together represents same for your application:
from turtle import Screen, Turtle
turtle1 = Turtle()
turtle2 = Turtle()
if turtle1.distance(turtle2) < 5: # pick distance that works for your app
# do stuff

Python Multiple Turtles Moving (seemingly) Simultaneously

I'm working on testing something for my teacher, he wants to see how the program below could possibly run faster if we simulated the simultaneous (i know it can't be perfectly simultaneous, this is just an experiment for the sake of learning/practicing) movement of multiple turtles. I've tried using modules like multiprocessing, threading, and even some crazy stupid attempt to time and delay (I'm in high school and I just learned about classes in python because of a previous question I asked I think last week)
So after many failed attempts I'm asking if someone has a few ideas of what else to try, or a direction to go in to simulate simultaneous movement of the turtles
import turtle
from turtle import Turtle
turtle.getscreen().delay(0)
class MyTurtle(Turtle):
def petal(self):
for i in range(90):
self.fd(1)
self.rt(1)
self.rt(90)
for i in range(90):
self.fd(1)
self.rt(1)
def stem(self):
self.pencolor('green')
self.fd(250)
def flowerhead(self):
for i in range(9):
self.pencolor('red')
self.begin_fill()
self.petal()
self.lt(230)
self.end_fill()
def stempetal(self):
self.seth(90)
self.rt(15)
self.fillcolor('green')
self.begin_fill()
self.petal()
self.end_fill()
tony = MyTurtle(shape='turtle')
todd = MyTurtle(shape='turtle')
tina = MyTurtle(shape='turtle')
tiny = MyTurtle(shape='turtle')
tweeny = MyTurtle(shape='turtle')
def flower1():
todd.speed('fastest')
todd.fillcolor('blue')
todd.flowerhead()
todd.seth(270)
todd.stem()
todd.stempetal()
def flower2():
tony.speed('fastest')
tony.setpos(80, -15)
tony.pencolor('green')
tony.goto(0, -200)
tony.fillcolor('purple')
tony.goto(80,-15)
tony.rt(40)
tony.flowerhead()
def flower3():
tina.speed('fastest')
tina.setpos(-80, -15)
tina.pencolor('green')
tina.goto(0, -200)
tina.fillcolor('teal')
tina.goto(-80,-15)
tina.lt(40)
tina.flowerhead()
def flower4():
tiny.speed('fastest')
tiny.setpos(160, -25)
tiny.pencolor('green')
tiny.goto(0, -200)
tiny.fillcolor('black')
tiny.goto(160, -25)
tiny.flowerhead()
def flower5():
tweeny.speed('fastest')
tweeny.setpos(-160, -25)
tweeny.pencolor('green')
tweeny.goto(0, -200)
tweeny.fillcolor('pink')
tweeny.goto(-160,-25)
tweeny.lt(40)
tweeny.flowerhead()
flower2()
tony.hideturtle()
flower4()
tiny.hideturtle()
flower3()
tina.hideturtle()
flower5()
tweeny.hideturtle()
flower1()
todd.hideturtle()
thank you for your time
The solution is to disable updating the position of each turtle, and then force the whole screen to update once the new position is computed.
import turtle
# our two turtle instances
first, second = turtle.Turtle(), turtle.Turtle()
first.tracer(False) # disable updating view on screen for this turtle!
second.tracer(False)
# make one move - note this will not appear on screen.
first.forward(50)
second.left(20)
# when you are ready to see the whole screen update
turtle.update()
To do what you want, you will have to essentially make it so that every new action is done before a turtle.update(). You cannot keep it to a serial execution as you are doing now - in other words, you can't run flower1, then flower2, in sequence.
Here's an example of a pair of turtles that will generate a random pattern on the screen at the same time:
import turtle
import random
# our two turtle instances
turtles = [turtle.Turtle(), turtle.Turtle()]
for turtle_object in turtles:
turtle_object.tracer(False)
for _ in range(10000): # make ten thousand moves.
for t in turtles:
# list the possible moves available
possible_moves = [t.forward, t.back, t.right, t.left]
# give it a random value
random_value = random.randint(0, 100)
# make a random move
random.choice(possible_moves)(random_value)
# update the whole screen now that the new positions have been calculated
turtle.update()
The trick here is to note that every new position for each turtle is calculated, then the screen as a whole is told to update, and only then do you move on to the next move. Every move must be as granular as possible.
You've asked for two different things, 'run faster' and 'simulate simultaneous movement'. I believe we can do both (separately) but I don't believe that tracer() and update() are the answer in this situation as they'd just be a band-aid to cover over the real issue.
wants to see how the program below could possibly run faster
If you want it to run faster, fix the bottleneck which is the petal() function. Replace it with something that uses turtle's built-in circle() function which is faster. For example:
def petal(self):
self.circle(-60, 90)
self.rt(90)
self.circle(-60, 90)
This speeds up your code by a factor of 25X with no other changes.
simulate simultaneous movement of the turtles
This can be done with turtle's own ontimer() event hander and some careful programming. Surprisingly, we use your original petal() logic as it breaks up the graphics into minute steps between which we can switch off processing to another timed event:
from random import randint
from turtle import Turtle, Screen
class MyTurtle(Turtle):
def petals(self, size=30, count=8, speed=100):
if size == 30:
self.begin_fill()
if size > 0: # drawing leading edge of petal
self.fd(3)
self.rt(3)
screen.ontimer(lambda: self.petals(size - 1, count, speed), speed)
return
if size == 0: # switch to other edge of petal
self.rt(90)
if size > -30: # drawing trailing edge of petal
self.fd(3)
self.rt(3)
screen.ontimer(lambda: self.petals(size - 1, count, speed), speed)
return
self.end_fill() # finish this petal
self.lt(230) # prepare for the next petal
if count > 0: # drawing the next petal
screen.ontimer(lambda: self.petals(count=count - 1, speed=speed), speed)
return
self.hideturtle() # finished drawing
def stem(self):
self.pencolor('green')
self.fd(250)
def flowerhead(self):
self.pencolor('red')
self.petals(speed=randint(50, 250))
def flower2():
tony.color('green', 'purple')
tony.penup()
tony.goto(0, -200)
tony.pendown()
tony.showturtle()
tony.goto(80, -15)
tony.rt(40)
tony.flowerhead()
def flower3():
tina.color('green', 'turquoise')
tina.penup()
tina.goto(0, -200)
tina.pendown()
tina.showturtle()
tina.goto(-80, -15)
tina.lt(40)
tina.flowerhead()
def flower5():
tweeny.color('green', 'pink')
tweeny.penup()
tweeny.goto(0, -200)
tweeny.pendown()
tweeny.showturtle()
tweeny.goto(-160, -25)
tweeny.lt(40)
tweeny.flowerhead()
tony = MyTurtle(shape='turtle', visible=False)
tina = MyTurtle(shape='turtle', visible=False)
tweeny = MyTurtle(shape='turtle', visible=False)
screen = Screen()
screen.ontimer(flower2, 100)
screen.ontimer(flower3, 120)
screen.ontimer(flower5, 100)
screen.mainloop()
RUNNING IMAGE
It won't be any faster, as it's just a simulation. (Well, it does go a little faster as I made petal drawing slightly cruder in return for speed.) If you look closely, you can see the turtles are (intentionally) moving at their own individual speed.

Using turtle in Python to draw six-pointed stars with different side lengths

Hopefully I'll be able to explain this well. I'm currently using helper functions to draw a six-pointed star in the turtle graphics window of python. First, we had to create a function to draw a triangle. Here is my code:
import turtle
wn = turtle.Screen()
tess = turtle.Turtle()
tess.speed(30)
def triangle(sz):
for i in range(3):
tess.fd(sz)
tess.lt(120)
Then, we had to use the triangle function to draw a six-pointed star. Here is my code:
def sixPtdStar(sz):
triangle(sz)
tess.lt(90)
tess.pu()
tess.fd(80)
tess.rt(90)
tess.fd(120)
tess.pd()
tess.rt(180)
triangle(sz)
Now, for me, this all runs smoothly. But the parameters for our test run of those two functions was that sz = 120 (so in the shell we'd type sixPtdStar(120) and it would run. But then we had to draw a row of stars with a new function, and then a BOX outline by those rows of stars, in another function. Here is my code:
def rowOfStars(numInRow,sz):
for i in range(numInRow):
sixPtdStar(sz)
tess.pu()
tess.lt(90)
tess.fd(80)
tess.lt(90)
def sqrOfRows(numInRow, sz):
for i in range(4):
rowOfStars(numInRow, sz)
tess.rt(90)
While this accomplishes the task, it only does so if the sz = 120. And for our test run on the rowOfStars function, the parameters are supposed to be (6, 72) and for the test run on the sqrOfRows function, our parameters are supposed to be (6, 36).
So my issue is this. How can I make this work no matter what sz equals? When I run it as is (with (6, 72) for rowOfStars or (6, 36) for sqrOfRows), the pen moves too far because the triangles aren't as big anymore.
Please let me know if more info is needed! Thanks! (I'm using Python 3.5.2)
Anywhere you use a unit that has a dimension:
tess.fd(80)
tess.fd(120) # probably should be tess.fd(sz)
tess.fd(80)
you need to scale it by what ever logic you used to get from 120 (sz) to 80. However, as #wptreanor mentioned, that logic is slightly flawed as the points on your star are uneven:
Also, your rowOfStars() routine doesn't really draw a row of stars (math is off and the pen is in the wrong state at times.) Simply fixing the scaling won't fix this. Finally, your sqrOfRows() routine won't work until rowOfStars() is fixed, and to make it useful, you need to adjust the starting position on the screen to make room for the drawing.
Below is my rework of your code to address some of these issues. It uses a slightly different calculation of how to position from finishing the lower to starting the upper triangle so the numbers are slightly different:
from turtle import Turtle, Screen
WIDTH_RATIO = 2 * 3**0.5 / 3 # ratio of widest point in star to edge of triangle
def triangle(size):
for i in range(3):
tess.fd(size)
tess.lt(120)
def sixPtdStar(size):
triangle(size)
tess.lt(30)
tess.pu()
tess.fd(size * WIDTH_RATIO)
tess.lt(150)
tess.pd()
triangle(size)
def rowOfStars(numInRow, size):
for i in range(numInRow):
sixPtdStar(size)
tess.pu()
tess.lt(90)
tess.fd(size * WIDTH_RATIO / 2)
tess.lt(90)
tess.pd()
def sqrOfRows(numInRow, size):
tess.pu()
halfSize = numInRow * size / 2
tess.goto(-halfSize, halfSize) # center on screen
tess.pd()
for i in range(4):
rowOfStars(numInRow, size)
tess.rt(90)
screen = Screen()
tess = Turtle()
tess.speed("fastest") # numbers > 10 are all equivalent, safer to use symbols
sqrOfRows(6, 36)
screen.exitonclick()
The problem is in your sixPtdStar() function.
def sixPtdStar(sz):
triangle(sz)
tess.lt(90)
tess.pu()
tess.fd(80) # here
tess.rt(90)
tess.fd(120) # and here
tess.pd()
tess.rt(180)
triangle(sz)
If your function takes a size as a parameter, all functions involving movement (such as forward() or goto()) need to be scaled by the size as well. The following code should work:
def sixPtdStar(sz):
triangle(sz)
tess.lt(90)
tess.pu()
tess.fd((2.0/3.0)*sz) #formerly 80
tess.rt(90)
tess.fd(sz) #formerly 120
tess.pd()
tess.rt(180)
triangle(sz)
This will ensure that all forward movements are proportional to the size of the object you create. You will need to make similar tweaks to your rowOfStars() function. I've also noticed that your six pointed star isn't fully symmetrical. You could resolve that by replacing tess.fd((2.0/3.0)*sz) with tess.fd((7.0/12.0)*sz).

How to change size of turtle?

I am trying to double the size of the turtle in the window every time I press x on my keyboard. I tried using .turtlesize(2,2,2), but that's not right. I need to double every time the key is pressed so if the turtle size is (1,1,1), it will become (2,2,2) then (4,4,4) and so on each time I press x.
This is what I have so far:
import turtle
turtle.setup(500,500)
wn = turtle.Screen()
wn.title("Commands")
wn.bgcolor("black")
tess = turtle.Turtle()
tess.shape("triangle")
tess.color("red")
tess.left(90)
def increaseSize():
size = tess.turtlesize()
increase = tuple([2 * num for num in size])
tess.turtlesize(increase) #this is where the error occurs
wn.onkey(increaseSize, "x")
wn.listen()
Change this line:
tess.turtlesize(increase)
to instead be:
tess.turtlesize(*increase)
turtlesize() wants three separate values but you were passing one tuple of three values so we need to spread that tuple across the argument list.
The default size of a Turtle object is 20 pixels, which is the equivalent of the ratio 1 when resizing the Turtle.
For example:
import turtle
tess = turtle.Turtle()
print(tess.shapesize())
Output:
(1.0, 1.0, 1)
The first two 1.0s in the tuple represents how many units 20 pixels the Turtle's width and height are, and the last 1 represents the width of the Turtle's outline.
You won't be able to see the outline if you only pass one argument into the tess.color() brackets, because by default, there is no outline.
To increase the Turtle's size, simply pass in the number of 20 pixels you want each of the Turtle's dimensions to be into tess.shapesize() or tess.turtesize():
import turtle
tess = turtle.Turtle()
tess.shapesize(2, 3, 1) # Sets the turtle's width to 60px and height to 90px
The other answer points out that the turtlesize function does not take in an
array; it takes in ints or floats, so you'll need to unpack the tuple with a *
when you pass the tuple into the function.
In your increaseSize function, the tuple and [] wrappers aren't necessary,
and only wastes efficiency. Simply use ():
def increaseSize():
size = tess.turtlesize()
increase = (2 * num for num in size)
tess.turtlesize(*increase)
On top of your code there is
turtle.setup(500,500)
wn = turtle.Screen()
Since you defined a Screen object, wn, it's cleaner to use wn.setup() instead of turtle.setup():
wn = turtle.Screen()
wn.setup(500,500)
All together:
import turtle
wn = turtle.Screen()
wn.setup(500,500)
tess = turtle.Turtle("triangle")
tess.color("red")
tess.left(90)
def increaseSize():
size = tess.turtlesize()
increase = (2 * num for num in size)
tess.turtlesize(*increase)
wn.onkey(increaseSize, "x")
wn.listen()
Output:

Categories

Resources