For a school project, I'm using Python Turtle to make an "avatar". I have curly hair, so I wrote some code to draw a black half-circle, every 10-ish degrees it stops, and makes a much smaller circle of the same color, then resumes.
The code works? It does what it's supposed to do for the first 3 smaller circles, but it seems to be random on the 4th smaller circle. I've even set the degrees to draw the half-circle to 10000 and it only completed the 4th smaller circle by 3/4ths.
import turtle
t = turtle.Turtle() #defining Turtle
def drawHair():
##debug, getting turtle to needed pos.
t.color("Moccasin")
for x in range (90):
t.forward(2.5)
t.left(1)
t.setheading(90)
##
t.color("Black")
cTime = 0 #"Timer" to determine a smaller "Curl"
for x in range (180): #SUPPOSED to draw a half-circle
t.forward(2.5) #
t.left(1) #
cTime = cTime + 1 ##For every "Degree" in the main half-circle,
##add 1 to the "Timer"
print("circle = " + str(cTime)) #debug
if cTime == 10: #If "Timer has reached it's limit"
cTime = 0 #Reset timer
for x in range (360): #Draw a full, smaller circle
t.forward(-0.4) #
t.left(1) #
I know this is more complicated than it should be. I simply want to know why this problem happens and how to fix it.
EDIT : https://imgur.com/a/uYe6UAb (Proof)
You are doing way too many draws, which repl.it seems to not like. There is actually a circle method in Turtle that draws circles (and semicircles) for you! This is a lot faster than drawing it with for loops.
Using this, and a bit of maths, I have come up with this code:
import turtle
from math import cos, sin, pi
t = turtle.Turtle() #defining Turtle
def drawHair():
##debug, getting turtle to needed pos.
t.color("Moccasin")
t.radians()
t.setheading(-pi / 2)
t.circle(140, extent=pi) # bottom semi circle
t.color("Black")
t.circle(140, extent=pi) # top semi circle
for x in range(19):
t.penup()
t.goto(cos(x*pi/18)*180+140, sin(x*pi/18)*180) # position for each curl
t.setheading(x*pi/18 + pi/2)
t.pendown()
t.circle(20)
drawHair()
I've basically used the parametric form of the equation for a circle. This is the result:
Tne problem may be that you're drawing circles that are too detailed for repl.it -- although your code should work, even Python turtle's own circle() function uses only 60 segments, not 360, to draw a circle. Even less for small circles.
Here's a rework of your code to draw all your circles in fewer segments, synchronized with your desire to draw the smaller circles every 10 degrees:
import turtle
def drawHair():
# get turtle to needed position
t.color("Moccasin")
for x in range(15):
t.forward(15)
t.left(6)
t.color("Black")
for x in range(36): # draw a half-circle
t.forward(12.5)
t.left(5)
if x % 2 == 0: # every other segment of the outer circle
for _ in range(72): # Draw a full, smaller circle
t.forward(-2)
t.left(5)
t.color("Moccasin") # finish the face outline
for x in range(15):
t.forward(15)
t.left(6)
t.hideturtle()
t = turtle.Turtle()
drawHair()
turtle.done()
I seems to work on repl.it for me. (Though repl.it does have lengthy pauses.) And the circles still appear round despite the reduced segments:
I assumed that you weren't allowed to use the turtle.circle() method, but if you can, as #Sweeper assumes, then this becomes a much simpler program:
import turtle
def drawHair():
# get turtle to needed position
t.color("Moccasin")
t.circle(143, 90)
t.color("Black")
for x in range(18): # draw a half-circle
t.circle(143, 5)
t.circle(-23) # draw a full, smaller circle
t.circle(143, 5)
t.color("Moccasin")
t.circle(143, 90)
t.hideturtle()
t = turtle.Turtle()
drawHair()
turtle.done()
You'll see the circles are slightly cruder than my first example but you can tune this using the steps parameter of turtle.circle().
Related
I’m new to programming and I’m reading a book called How To Think Like A Computer Scientist. In the fourth chapter, it talks about functions.
At the end of the chapter, there’s an exercise that asks me to draw the following pattern using Python’s turtle module.
I was examining this picture and decided to split it into two: 1) the lines in the middle and 2) the squares that go on top of each other like a spiral.
I drew the first part using this code:
import turtle
wn = turtle.Screen() # Set up the window
wn.bgcolor("lightgreen")
alex = turtle.Turtle() # Create Alex
alex.color("blue")
alex.pensize(3)
for i in range(20): # Here I start drawing the lines
alex.forward(100)
alex.backward(100)
alex.left(360/20) # Fit 20 lines in the 360 degree circle
wn.mainloop()
When I run it, it draws this:
Then, I created the draw_square function and managed to draw the first square:
import turtle
def draw_square(turtle, size):
for i in range(4):
turtle.forward(size)
turtle.left(90)
wn = turtle.Screen() # Set up the window
wn.bgcolor("lightgreen")
alex = turtle.Turtle() # Create Alex
alex.color("blue")
alex.pensize(3)
for i in range(20): # Here I start drawing the lines
alex.forward(100)
alex.backward(100)
alex.left(360/20) # Fit 20 lines in the 360 degree circle
# In a messy way, using what I've learned, I move Alex to where he's supposed to be now
# I'm pretty sure there's a classier way to do this
alex.penup()
alex.backward(100)
alex.right(90)
alex.forward(100)
alex.left(90)
alex.pendown()
# Here I get Alex to draw the square
draw_square(alex, 200)
wn.mainloop()
When I run it, it draws this:
Now I’m stuck. I don’t know where to go from here. I don’t know how to go about drawing all the other squares. I can’t figure out where to place the turtle, and how many degrees to tilt the square (probably 20, like the lines, but I don’t know how to implement it)… Anyways, you guys got any tips? Any suggestions?
I’m trying not to skip any exercises on the book and this one got me.
Excellent attempt, and thanks for the clear images of expected/actual output!
The pattern is actually a bit simpler than you may think. A single box is being drawn from the center repeatedly, with the turtle pivoting slightly on the center point on each iteration. The overlaps of the box sides create the illusion of "spokes".
As for determining the turn amount in degrees, I took 360 and divided it by the number of spokes shown in the image (20), giving 18 degrees.
Here's code that produces the correct output.
import turtle
def draw_square(turtle, size):
for i in range(4):
turtle.forward(size)
turtle.left(90)
if __name__ == "__main__":
wn = turtle.Screen()
wn.bgcolor("lightgreen")
alex = turtle.Turtle()
alex.color("blue")
alex.pensize(3)
boxes = 20
for _ in range(boxes):
draw_square(alex, 200)
alex.left(360 / boxes)
wn.mainloop()
Output:
Recently, I figured out on python how to make a moving line.
What I want to do now is know how to do a clock animation with a moving 2nd hand (Minutes) and without the hour hand.
My current stage of the moving line animation:
What I'm trying to achieve:
since they're screenshots they don't show them moving
my code
import turtle
import time
turtle.hideturtle()
def draw_line(x,y,heading,length,color):
turtle.up()
turtle.goto(x,y)
turtle.down()
turtle.setheading(heading)
turtle.color(color)
turtle.fd(length)
turtle.tracer(0,0)
for heading in range(0,-360*100,-6):
turtle.clear()
draw_line(0,0,heading,200,'blue')
turtle.update()
time.sleep(1)
it'd help also if someone provided how to draw the clock
It's not clear what you're asking but I'm assuming it's two things: first, how to add another moving line; second, how to set the angles of these lines to match the actual (local) time. I've reworked your code below to do both these things, and threw in an hour hand as well as the cost is low. Something to consider:
You don't want to do this with a single turtle--you want at least two. Otherwise, when you call clear(), you lose everything, including the dial, and have to redraw it. You can work around this by either using undo(), or, more simply, have one turtle whose drawings are permanent (the dial) and another whose drawings are cleared on every tick (the hands).
Other changes below include: tossing time.sleep() in favor of turtle's own ontimer() event; switching from turtle's function-based API to its object-oriented one so we can manage two turtles. And I switched turtle into Logo mode which puts zero degrees at the top of the screen, and makes angles for graphics be clockwise instead of counter-clockwise (which is useful if you're implementing a clock!)
from turtle import Screen, Turtle, Vec2D
from time import localtime
CENTER = Vec2D(0, 0)
def draw_line(position, heading, length, color):
hands.up()
hands.goto(position)
hands.down()
hands.setheading(heading)
hands.color(color)
hands.forward(length)
def tick():
time = localtime()
second_heading = time.tm_sec * 6
minute_heading = time.tm_min * 6 + second_heading / 60
hour_heading = time.tm_hour % 12 * 30 + minute_heading / 12
hands.clear()
draw_line(CENTER, second_heading, 300, 'red')
draw_line(CENTER, minute_heading, 200, 'blue')
draw_line(CENTER, hour_heading, 100, 'green')
screen.update()
screen.ontimer(tick, 1000)
screen = Screen()
screen.mode("logo") # 0 degrees at top, clockwise angles!
screen.tracer(False) # force manual screen updates
# What this turtle draws is "permanent"
dial = Turtle(visible=False)
dial.penup()
dial.dot()
dial.setx(330) # remember mode is "logo"
dial.pendown()
dial.circle(330)
# What this turtle draws has to be redrawn on every tick
hands = Turtle(visible=False)
tick()
screen.mainloop()
My task is to write a function, drawCircle(radius,fillColor), that asks a user for the specific radius of the circle and which color they'd like the circle to be filled.
I have the circle-drawing down, but I'm struggling with getting the circle to fill with the user-defined color. Any help would be greatly appreciated.
import turtle
def drawCircle(radius, fillColor):
x=360/300 #This gives the angle
r=radius#This is the radius of the circle.
c=fillColor
c=str("")
z=1 #Placeholder for the while loop.
win=turtle.Screen()
tom=turtle.Turtle()
fillColor=tom.color()
tom.begin_fill()
while (z<=300):
tom.forward(r)
tom.right(x)
tom.forward(r)
z=z+1
win.exitonclick()
tom.end_fill()
This is my function call: drawCircle(1,"red")
There are several problems with your code:
You call win.exitonclick before tom.end_fill, so programm exits before filling (as it happens on end_fill)
You do "fillColor=tom.color()" with gets you current color. Instead use "tom.fillcolor(fillColor)"
Unnecessary copying of variables radius->r and fillColor->c
This is python. Use for whenever possible. Instead of counting using z use:
for _ in range(300):
My final code:
import turtle
def drawCircle(radius, fillColor):
x = 360/300 # This gives the angle
win = turtle.Screen()
tom = turtle.Turtle()
tom.fillcolor(fillColor)
tom.begin_fill()
for _ in range(300):
tom.forward(radius)
tom.right(x)
tom.forward(radius)
tom.end_fill()
win.exitonclick()
drawCircle(1, "red")
I have the circle-drawing down
Before addressing your fill issue, I'd argue that your premise isn't true, you don't have circle-drawing down. As #Mysak0CZ shows, your circle of radius 1 is huge -- 1 what? You're drawing a circle but have no real control over its size.
As a professional turtle wrangler, I'd go about the problem as follows. Not only your angle needs to be divided by the number of segments you plan to draw, but you need to compute a circumference based on the requested radius and chop that up as well. I do so below and include a call to turtle's own .circle() method to show that we're in the right ballpark. And I fix your minor fill issue:
import math
from turtle import Turtle, Screen # force object-oriented turtles
SEGMENTS = 60 # how many lines make up the circle
def drawCircle(radius, fillColor):
distance = math.pi * radius * 2 / SEGMENTS # circumference / SEGMENTS
angle = 360 / SEGMENTS
turtle.fillcolor(fillColor)
turtle.begin_fill()
for _ in range(SEGMENTS):
turtle.forward(distance)
turtle.left(angle) # left for .circle() compatibility
turtle.end_fill()
screen = Screen()
turtle = Turtle()
drawCircle(100, 'red')
turtle.circle(100) # for comparison
screen.exitonclick()
Here is my code for a pink circle if you are going through the same Python learning resource. I think the original code was missing an argument.
import turtle
def drawPolygon(t, sideLength, numSides):
t.goto(0, 0)
turnAngle = 360 / numSides
for i in range(numSides):
t.forward(sideLength)
t.right(turnAngle)
def drawCircle(anyTurtle, radius):
circumference = 2 * 3.1415 * radius
sideLength = circumference / 360
drawPolygon(anyTurtle, sideLength, 360)
def drawFilledCircle(anyTurtle, radius, color):
anyTurtle.fillcolor(color)
anyTurtle.begin_fill()
drawCircle(anyTurtle, radius)
anyTurtle.end_fill()
anyTurtle.hideturtle()
wn = turtle.Screen()
wheel = turtle.Turtle()
drawFilledCircle(wheel, 80, "pink")
wn.exitonclick()
Here's the exercise Lessons from a Triangle!
import turtle
def drawPolygon(t, sideLength, numSides):
turnAngle = 360 / numSides
for i in range(numSides):
t.forward(sideLength)
t.left(turnAngle)
def drawFilledCircle(anyTurtle, radius, vcolor):
anyTurtle.fillcolor(vcolor)
anyTurtle.begin_fill()
circumference = 2 * 3.1415 * radius
sideLength = circumference / 360
drawPolygon(anyTurtle, sideLength, 360)
anyTurtle.end_fill()
wn = turtle.Screen()
wheel = turtle.Turtle()
drawFilledCircle(wheel, 20, 'purple')
wn.exitonclick()
1) How come when I try to change the speed of the turtle like wheel.speed(10) it doesn't work? What's the default speed if I don't indicate the speed?
2) How do i put my turtle to the middle of the circle once it's done?
Thank You so Much!
As abarnert said, the link to your exercise is broken, but there are a few things you can do to speed up your program. FWIW, the turtle module has a .circle method, but I guess the point of your exercise is to use a polygon to approximate a circle.
wheel.speed(10) does work (although wheel.speed(0) is even faster). It doesn't appear to make much difference because the sides of your 360-sided polygon are so tiny, and so the turtle has to do several moves & turns just to traverse a single pixel.
Using a 360-sided polygon to draw a circle, no matter what the radius is, is not a great strategy. The code below determines the number of sides from the circumference so that each side is approximately 1 pixel. This will look good for a circle of any radius, and it avoids doing too much work for small circles.
Apart from using turtle.speed to speed things up you can also reduce the canvas's frame delay. Also, if you hide the turtle while drawing, it'll go even faster.
The following code was tested on Python 2.6.6; it should also work without modification on Python 3.x, but on Python 3.x you may remove the from __future__ import division, print_function line.
#!/usr/bin/env python
''' Approximate a circle using a polygon
From http://stackoverflow.com/q/30475519/4014959
Code revised by PM 2Ring 2015.05.27
'''
from __future__ import division, print_function
import turtle
def drawPolygon(t, sideLength, numSides):
turnAngle = 360 / numSides
for i in range(numSides):
t.forward(sideLength)
t.left(turnAngle)
def drawFilledCircle(anyTurtle, radius, vcolor):
anyTurtle.fillcolor(vcolor)
anyTurtle.begin_fill()
circumference = 2 * 3.14159 * radius
#Calculate number of sides so that each side is approximately 1 pixel
numSides = int(0.5 + circumference)
sideLength = circumference / numSides
print('side length = {0}, number of sides = {1}'.format(sideLength, numSides))
drawPolygon(anyTurtle, sideLength, numSides)
anyTurtle.end_fill()
wn = turtle.Screen()
#Set minimum canvas update delay
wn.delay(1)
wheel = turtle.Turtle()
#Set fastest turtle speed
wheel.speed(0)
#wheel.hideturtle()
radius = 20
#Move to the bottom of the circle, so
#that the filled circle will be centered.
wheel.up()
wheel.goto(0, -radius)
wheel.down()
drawFilledCircle(wheel, radius, 'purple')
#Move turtle to the center of the circle
wheel.up()
wheel.goto(0, 0)
#wheel.showturtle()
wn.exitonclick()
I am a begginner at python and I'm trying to make a circle game. So far it draws a circle at your mouse with a random color and radius when you click.
Next, I would like the circle to fly off the screen in a random direction. How would I go about doing this? This is the main chunk of my code so far:
check1 = None
check2 = None
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit
if event.type == MOUSEBUTTONDOWN:
last_mouse_pos = pygame.mouse.get_pos()
if last_mouse_pos:
global check
color1 = random.randint(0,255)
color2 = random.randint(0,255)
color3 = random.randint(0,255)
color = (color1,color2,color3)
radius = random.randint (5,40)
posx,posy = last_mouse_pos
if posx != check1 and posy != check2:
global check1, check2
screen.lock()
pygame.draw.circle(screen, color, (posx,posy), radius)
screen.unlock()
check1,check2 = posx,posy
pygame.display.update()
Again, I want the circle to fly off the screen in a random direction.
I have made a few attempts but no successes yet.
Also, thanks to jdi who helped me s
When you create the circle (on click), generate 2 random numbers. These will be your (x,y) components for a two dimensional Euclidean velocity vector:
# interval -1.0 to 1.0, adjust as necessary
vx, vy = ( (random.random()*2) -1, (random.random()*2) - 1 )
Then after the ball has been created, on each iteration of the game loop, increment your ball's position by the velocity vector:
posx, posy = posx + vx, posy + vy
Note that the overall speed might be variable. If you want to always have a speed of 1.0 per seconds, normalize the vector:
To normalize the vector, you divide it by its magnitude:
So in your case:
And hence:
So in Python, after importing math with import math:
mag = math.sqrt(vx*vx + vy*vy)
v_norm = vx/mag, vy/mag
# use v_norm instead of your (vx, vy) tuple
Then you can multiply this with some speed variable, to get reliable velocity.
Once you progress to having multiple objects moving around and potentially off screen, it is useful to remove the offscreen objects which have no chance of coming back, and have nothing to do with your program anymore. Otherwise, if you keep tracking all those offscreen objects while creating more, you get essentially a memory leak, and will run out of memory given enough time/actions.
While what you are doing right now is quite simple, assuming you haven't already, learning some basic vector math will pay itself off very soon. Eventually you may need to foray into some matrix math to do certain transformations. If you are new to it, its not as hard as it first looks. You can probably get away with not studying it, but you will save yourself effort later if you start attempting to do more ambitious things.
Right now, you are doing the following (drastically simplifying your code)...
while True:
if the mouse was clicked:
draw a circle on the screen where the mouse was clicked
Let's make things a little easier, and build up gradually.
Start with the circle without the user clicking
To keep things simple, let's make the circle near the top left of the screen, that way we can always assume there will be a circle (making some of the logic easier)
circle_x, circle_y = 10,10
while True:
draw the circle at circle_x, circle_y
pygame.display.update()
Animate the circle
Before going into too much about "random directions", let's just make it easy and go in one direction (let's say, always down and to the right).
circle_x, circle_y = 0,0
while True:
# Update
circle_x += 0.1
circle_y += 0.1
# Draw
draw the circle at circle_x, circle_y
update the display
Now, every time through the loop, the center of the circle moves a bit, and then you draw it in its new position. Note that you might need to reduce the values that you add to circle_x and y (in my code, 0.1) in case the circle moves too fast.
However, you'll notice that your screen is now filling up with circles! Rather than one circle that is "moving", you're just drawing the circle many times! To fix this, we're going to "clear" the screen before each draw...
screen = ....
BLACK = (0,0,0) # Defines the "black" color
circle_x, circle_y = 0,0
while True:
# Update
circle_x += 0.1
circle_y += 0.1
# Draw
screen.fill(BLACK)
draw the circle at circle_x, circle_y
update the display
Notice that we are "clearing" the screen by painting the entire thing black right before we draw our circle.
Now, you can start work the rest of what you want back into your code.
Keep track of multiple circles
You can do this by using a list of circles, rather than two circle variables
circles = [...list of circle positions...]
while True:
# Update
for circle in circles:
... Update the circle position...
# Draw
screen.fill(BLACK)
for circle in circles:
draw the circle at circle position # This will occur once for each circle
update the display
One thing to note is that if you keep track of the circle positions in a tuple, you won't be able to change their values. If you're familiar with Object Oriented Programming, you could create a Circle class, and use that to keep track of the data relating to your circles. Otherwise, you can every loop create a list of updated coordinates for each circle.
Add circle when the user clicks
circles = []
while True:
# event handling
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
circles.append( pos ) # Add a new circle to the list
# Update all the circles
# ....
# Draw
clear the screen
for circle_position in circles:
draw the circle at circle_position # This will occur once for each circle
update the display
Have the circle move in a random direction
This is where a good helping of math comes into play. Basically, you'll need a way to determine what to update the x and y coordinate of the circle by each loop. Keep in mind it's completely possible to just say that you want it to move somewhere between -1 and 1 for each axis (X, y), but that isn't necessarily right. It's possible that you get both X and Y to be zero, in which case the circle won't move at all! The next Circle could be 1 and 1, which will go faster than the other circles.
I'm not sure what your math background is, so you might have a bit of learning to do in order to understand some math behind how to store a "direction" (sometimes referred to as a "vector") in a program. You can try Preet's answer to see if that helps. True understanding is easier with a background in geometry and trigonometry (although you might be able to get by without it if you find a good resource).
Some other thoughts
Some other things you'll want to keep in mind:
Right now, the code that we're playing with "frame rate dependent". That is, the speed in which the circles move across the screen is entirely dependent on how fast the computer can run; a slower computer will see the circles move like snails, while a faster computer will barely see the circles before they fly off the screen! There are ways of fixing this, which you can look up on your own (do a search for "frame rate dependence" or other terms in your favorite search engine).
Right now, you have screen.lock() and screen.unlock(). You don't need these. You only need to lock/unlock the screen's surface if the surface requires it (some surfaces do not) and if you are going to manually access the pixel data. Doing things like drawing circles to the screen, pygame in lock/unlock the surfaces for you automatically. In short, you don't need to deal with lock/unlock right now.