Creating an irregular arc with python turtle - python

I'm trying to make a function which draws an irregular arc similar to figure 1, instead it has drawn a spiral. I'm not sure how to draw one correctly and there are no functions to do this to my knowledge
Long Arc - Figure 1
import turtle
char = turtle.Turtle()
char.speed(0)
screen = turtle.Screen()
screen.tracer(False)
def draw_arc(length, left_right):
sx = char.xcor()
sy = char.ycor()
def turn(angle):
if left_right:
char.left(angle)
else:
char.right(angle)
count = 1.8
turn(90)
char.forward(1)
while char.xcor() != sx and char.ycor() != sy and count >= 0:
char.forward(1)
turn(1 * count)
count -= 0.01
draw_arc(100, True)
screen.update()
turtle.listen()
turtle.mainloop()

import turtle
t = turtle.Pen(visible=False)
t.speed('fastest')
t.left(90)
for x in range(180):
t.forward(1)
t.right(1)
add this line and modify it until it fit ur need ( setx() or sety )
t.setx( x * 1.5)

Related

Is there a way to check a radius around a random point in order for random points to be separate from each other

I'm currently creating a drawing project, where I use random to create random star points in the sky. I currently have the ability for two points to not be the same, but I would like to find a way to make it so they wouldn't land in a x radius circle. Is there any way in python to complete this
import turtle as t, random as r
screen=t.Screen()
screen.bgcolor("#3A3B3C")
t.speed(1)
def randomStars(y, num):
t.penup()
t.pensize(2)
t.color("white")
locations = []
for x in range(num):
repeat = True
t.penup()
t.seth(90)
y_pos = starLocationY(y)
x = starLocationX()
while repeat == True:
if [x,y_pos] in locations:
y_pos = starLocationY(y)
x = starLocationX()
else:
locations.append([x,y_pos])
repeat = False
t.goto(x,y_pos)
t.pendown()
t.seth(60)
t.fd(2)
t.fd(-2)
t.seth(-60)
t.fd(2)
t.fd(-2)
t.seth(240)
t.fd(2)
t.fd(-2)
t.seth(120)
t.fd(2)
t.fd(-2)
randomStars(85,30)
p.s: I'm using trinket for the project, as required by the class, so the modules are limited
link to trinket:https://trinket.io/python/9776ba1b8a
We can use any on a generator invoking turtle's distance() method on the elements of your locations list. Easier than it sounds:
from turtle import Screen, Turtle
from random import randint
EXCLUSION_RADIUS = 35 # in pixels
def starLocationY(y):
return randint(y, 190)
def starLocationX():
return randint(-190, 190)
def randomStars(y, number):
turtle.penup()
turtle.pensize(2)
turtle.color('white')
locations = []
for _ in range(number):
y_pos = starLocationY(y)
x = starLocationX()
while True:
turtle.goto(x, y_pos)
if any(turtle.distance(location) < EXCLUSION_RADIUS for location in locations):
y_pos = starLocationY(y)
x = starLocationX()
else:
locations.append((x, y_pos))
break
turtle.pendown()
for heading in range(0, 360, 120):
turtle.setheading(heading)
turtle.forward(2)
turtle.backward(4)
turtle.forward(2)
turtle.penup()
screen = Screen()
screen.bgcolor('#3A3B3C')
turtle = Turtle()
turtle.hideturtle()
turtle.speed('fastest') # because I have no patience
randomStars(85, 20)
screen.exitonclick()

How to draw a dotted circle using python Turtle package

Something similar to the picture attached. I tried modifying the code I got online. But my code goes to infinity loop
[1]: https://i.stack.imgur.com/HfKog.png
def draw_circle(radius):
turtle.up()
turtle.goto(0, radius) # go to (0, radius)
turtle.pendown() # pen down
times_y_crossed = 0
x_sign = 1.0
while times_y_crossed <= 1:
turtle.dot(5, "Red")
turtle.forward(5) # move by 1/360
turtle.right(1.0)
turtle.penup()
turtle.forward(5) # move by 1/360
turtle.right(1.0)
x_sign_new = math.copysign(1, turtle.xcor())
if x_sign_new != x_sign:
times_y_crossed += 10
x_sign = x_sign_new
turtle.up() # pen up
return
There are some issues with your code, like it does not count with the radius and the color for the pen was not set and as I've checked it did a half circle for me.
I show you a simple working example, first a dashed version because your original code looks like you wanted a dashed circle
import turtle
import math
def draw_circle_dashed(radius):
turtle.up()
turtle.goto(0, radius) # go to (0, radius)
times_y_crossed = 0
dist=2*math.pi*radius/360
turtle.pencolor("red")
for _ in range(180):
turtle.pendown()
turtle.forward(dist) # move by 1/360
turtle.right(1.0)
turtle.penup()
turtle.forward(dist) # move by 1/360
turtle.right(1.0)
turtle.up() # pen up
return
draw_circle_dashed(200)
and a dotted variant as well because of the question title
import turtle
import math
def draw_circle_dotted(radius):
turtle.up()
turtle.goto(0, radius) # go to (0, radius)
dist=2*math.pi*radius/360
for _ in range(360):
turtle.dot(2,"red")
turtle.forward(dist) # move by 1/360
turtle.right(1.0)
turtle.up() # pen up
return
draw_circle_dotted(300)
We can use turtle's own circle() method to do this as we can arbitrarily start and stop it, leaving behind dots as we do:
from turtle import Screen, Turtle
from math import pi
DOT_DIAMETER = 5
def draw_circle(radius):
turtle.penup()
circumference = 2 * pi * radius
dot_extent = 360 * DOT_DIAMETER*2 / circumference # diameter to angle
extent = 0
while extent < 360:
turtle.dot(DOT_DIAMETER)
turtle.circle(radius, extent=dot_extent)
extent += dot_extent
screen = Screen()
turtle = Turtle()
turtle.color('red')
draw_circle(100)
turtle.hideturtle()
screen.exitonclick()
Using what we learned from that exercise, let's now fix your code. The issue I see is this:
turtle.right(1.0)
The angle to turn is dependent on the dot diameter, but we actually have to calculate it:
from turtle import Screen, Turtle
from math import pi, copysign
DOT_DIAMETER = 5
def draw_circle(radius):
turtle.penup()
turtle.sety(radius)
diameter = 2 * radius
circumference = pi * diameter
dot_extent = 360 * DOT_DIAMETER / circumference # diameter to angle
times_y_crossed = 0
x_sign = 1
while times_y_crossed < 2:
turtle.dot(DOT_DIAMETER, 'red') # draw the dot
turtle.right(dot_extent)
turtle.forward(DOT_DIAMETER)
turtle.right(dot_extent) # draw the gap
turtle.forward(DOT_DIAMETER)
x_sign_new = copysign(1, turtle.xcor())
if x_sign_new != x_sign:
times_y_crossed += 1
x_sign = x_sign_new
turtle.pendown()
screen = Screen()
turtle = Turtle()
turtle.color('red')
draw_circle(100)
turtle.hideturtle()
screen.exitonclick()
By not calculating the angle, and using a fixed angle of 1.0, you were off by a factor of two.

Screen Won't Open When Drawing Checker Board Using Turtle

I am trying to draw a checker board using the Turtle library and am running into an error where the board window does not open. It was working at the beginning of my session about 30 minutes ago but, I changed some stuff and want to know why it changed.
Here is my code:
##This program draws a checkboard using the turtle library
import turtle
#below initiates the turtle pen and screen
penMain = turtle.Turtle()
turtleMain = turtle.Screen()
def turtleBoard():
for x in range(4):
penMain.forward(30)
penMain.left(90)
penMain.forward(30)
turtleMain.setup(600, 600)
penMain.speed(50)
for a in range(8):
penMain.up()
penMain.setpos(0, 30 * a)
penMain.down()
for x in range(8):
if (a + x)% 2 == 0:
squareColor = 'black'
else:
squareColor = 'white'
penMain.fillcolor(squareColor)
penMain.begin_fill()
turtleBoard()
penMain.end_fill()
I believe this code works besides my one error! Thank you all for your help in advance!
I can't say what changes you made to get your current code, but this code seems to be working:
##This program draws a checkboard using the turtle library
import turtle
#below initiates the turtle pen and screen
penMain = turtle.Turtle()
turtleMain = turtle.Screen()
def turtleBoard():
penMain.forward(30)
turtleMain.setup(600, 600)
penMain.speed(50)
for a in range(8):
for x in range(8):
penMain.up()
penMain.setpos(30 * x, 30 * a)
penMain.down()
penMain.begin_fill()
for xx in range(4):
penMain.forward(30)
penMain.left(90)
if a%2 == x%2:
squareColor = 'black'
else:
squareColor = 'white'
penMain.fillcolor(squareColor)
penMain.end_fill()
turtleBoard()
turtle.done()
Now that we've seen that your code can be made to work, let's consider stamping instead of drawing to make it work more simply and more quickly:
from turtle import Screen, Turtle
SQUARES_PER_EDGE = 8
SQUARE_SIZE = 30 # in pixels
OFFSET = SQUARE_SIZE * (SQUARES_PER_EDGE / 2) - SQUARE_SIZE/2 # center the board
CURSOR_SIZE = 20
def turtleBoard():
turtle.shape('square')
turtle.shapesize(SQUARE_SIZE / CURSOR_SIZE)
turtle.penup()
for y in range(SQUARES_PER_EDGE):
for x in range(SQUARES_PER_EDGE):
turtle.goto(x * SQUARE_SIZE - OFFSET, y * SQUARE_SIZE - OFFSET)
turtle.fillcolor('black' if y % 2 == x % 2 else 'white')
turtle.stamp()
screen = Screen()
screen.setup(600, 600)
turtle = Turtle()
turtle.speed('fastest') # because I have no patience
turtleBoard()
screen.exitonclick()
I lined up the indent of the bottom 4 lines with the last 'else' statement and it worked. Thank you guys!

Trying to make dots orbit bigger dot

So I've been attempting to make some dots not only come towards a circle but also to make them orbit it. To do this I am using cosine and sine, however I'm running into issues with getting the dots to move forward as well as setting their distance. With the code below the dots are able to form a circle around the bigger dot, as well as follow it, but they don't approach the dot nor do they, when having the coordinates scaled by their distance from t1, come to that location, but instead do funky stuff. This is referring specifically to the line
t2.goto(2 * (t1.xcor() + math.degrees(math.cos(math.radians(t1.towards(t2)))) // 1), 2 * (t1.ycor() + math.degrees(math.sin(math.radians(t1.towards(t2)))) // 1))
which I had replaced with:
t2.goto(dist * (t1.xcor() + math.degrees(math.cos(math.radians(t1.towards(t2)))) // 1), dist * (t1.ycor() + math.degrees(math.sin(math.radians(t1.towards(t2)))) // 1))
and that gave me the sporadic view of the dots attempting to follow the bigger dot.
This line is found in the follow() function. Create() makes the smaller dots, move() moves the bigger dot and grow() grows the bigger dot on collision with the smaller dots. Produce() and redraw() are supposed to be a stage 2 of the program, but those functions are irrelevant to the question. Finally, quit() just exits the Screen() and quits the program.
Thanks to cdlane for help with organizing data and updating the screen more efficiently.
Code as of now:
from turtle import Turtle, Screen
import sys
import math
CURSOR_SIZE = 20
def move(x, y):
""" has it follow cursor """
t1.ondrag(None)
t1.goto(x, y)
screen.update()
t1.ondrag(move)
def grow():
""" grows t1 shape """
global t1_size, g
t1_size += 0.1
t1.shapesize(t1_size / CURSOR_SIZE)
g -= .1
t1.color((r/255, g/255, b/255))
screen.update()
def follow():
""" has create()'d dots follow t1 """
global circles, dist
new_circles = []
for (x, y), stamp in circles:
t2.clearstamp(stamp)
t2.goto(x, y)
dist = t2.distance(t1) / 57.29577951308232 // 1
t2.goto(2 * (t1.xcor() + math.degrees(math.cos(math.radians(t1.towards(t2)))) // 1), 2 * (t1.ycor() + math.degrees(math.sin(math.radians(t1.towards(t2)))) // 1))
t2.setheading(t2.towards(t1))
if t2.distance(t1) < t1_size // 1:
if t2.distance(t1) > t1_size * 1.2:
t2.forward(500/t2.distance(t1)//1)
else:
t2.forward(3)
if t2.distance(t1) > t1_size // 2:
new_circles.append((t2.position(), t2.stamp()))
else:
grow() # we ate one, make t1 fatter
screen.update()
circles = new_circles
if circles:
screen.ontimer(follow, 10)
else:
phase = 1
produce()
def create():
""" create()'s dots with t2 """
count = 0
nux, nuy = -400, 300
while nuy > -400:
t2.goto(nux, nuy)
if t2.distance(t1) > t1_size // 2:
circles.append((t2.position(), t2.stamp()))
nux += 20
count += 1
if count == 40:
nuy -= 50
nux = -400
count = 0
screen.update()
def quit():
screen.bye()
sys.exit(0)
def redraw():
t2.color("black")
t2.shapesize((t2_size + 4) / CURSOR_SIZE)
t2.stamp()
t2.shapesize((t2_size + 2) / CURSOR_SIZE)
t2.color("white")
t2.stamp()
def produce():
#create boundary of star
global t2_size, ironmax
t1.ondrag(None)
t1.ht()
t2.goto(t1.xcor(), t1.ycor())
t2.color("black")
t2.shapesize((t1_size + 4) / CURSOR_SIZE)
t2.stamp()
t2.shapesize((t1_size + 2) / CURSOR_SIZE)
t2.color("white")
t2.stamp()
#start producing helium
while t2_size < t1_size:
t2.color("#ffff00")
t2.shapesize(t2_size / 20)
t2.stamp()
t2_size += .1
redraw()
screen.update()
ironmax = t2_size
t2_size = 4
while t2_size < ironmax:
t2.shapesize(t2_size / 20)
t2.color("grey")
t2.stamp()
t2_size += .1
screen.update()
# variables
t1_size = 6
circles = []
phase = 0
screen = Screen()
screen.screensize(900, 900)
#screen.mode("standard")
t2 = Turtle('circle', visible=False)
t2.shapesize(4 / CURSOR_SIZE)
t2.speed('fastest')
t2.color('purple')
t2.penup()
t2_size = 4
t1 = Turtle('circle')
t1.shapesize(t1_size / CURSOR_SIZE)
t1.speed('fastest')
r = 190
g = 100
b = 190
t1.color((r/255, g/255, b/255))
t1.penup()
t1.ondrag(move)
screen.tracer(False)
screen.listen()
screen.onkeypress(quit, "Escape")
create()
follow()
#print(phase)
screen.mainloop()
I took another crack at this, just looking at the problem of meteors swarming around a planet. Or in this case, moon as I chose Deimos as my model. I attempted to work at scale making the coordinate system 1 pixel = 1 kilometer. At the start, Deimos sits in a field of meteors each of which has a random heading but they all have the same size and velocity:
from turtle import Turtle, Screen
from random import random
METEOR_VELOCITY = 0.011 # kilometers per second
METEOR_RADIUS = 0.5 # kilometers
SECONDS_PER_FRAME = 1000 # each updates represents this many seconds passed
UPDATES_PER_SECOND = 100
DEIMOS_RADIUS = 6.2 # kilometers
G = 0.000003 # Deimos gravitational constant in kilometers per second squared
CURSOR_SIZE = 20
def follow():
global meteors
new_meteors = []
t = SECONDS_PER_FRAME
for (x, y), velocity, heading, stamp in meteors:
meteor.clearstamp(stamp)
meteor.goto(x, y)
meteor.setheading(heading)
meteor.forward(velocity * t)
meteor.setheading(meteor.towards(deimos))
meteor.forward(G * t * t)
meteor.setheading(180 + meteor.towards(x, y))
if meteor.distance(deimos) > DEIMOS_RADIUS * 2:
new_meteors.append((meteor.position(), velocity, meteor.heading(), meteor.stamp()))
screen.update()
meteors = new_meteors
if meteors:
screen.ontimer(follow, 1000 // UPDATES_PER_SECOND)
def create():
""" create()'s dots with meteor """
count = 0
nux, nuy = -400, 300
while nuy > -400:
meteor.goto(nux, nuy)
if meteor.distance(deimos) > DEIMOS_RADIUS * 2:
heading = random() * 360
meteor.setheading(heading) # all meteors have random heading but fixed velocity
meteors.append((meteor.position(), METEOR_VELOCITY, meteor.heading(), meteor.stamp()))
nux += 20
count += 1
if count % 40 == 0:
nuy -= 50
nux = -400
screen.update()
meteors = []
screen = Screen()
screen.screensize(1000, 1000)
screen.setworldcoordinates(-500, -500, 499, 499) # 1 pixel = 1 kilometer
meteor = Turtle('circle', visible=False)
meteor.shapesize(2 * METEOR_RADIUS / CURSOR_SIZE)
meteor.speed('fastest')
meteor.color('purple')
meteor.penup()
deimos = Turtle('circle')
deimos.shapesize(2 * DEIMOS_RADIUS / CURSOR_SIZE)
deimos.color("orange")
deimos.penup()
screen.tracer(False)
create()
follow()
screen.mainloop()
The first variable to investigate is METEOR_VELOCITY. At the setting provided, most meteors will crash into the moon but a few obtain orbital velocity. If you halve its value, all meteors will crash into the moon. If you double its value, a few meteors obtain escape velocity, leaving the window; a few may crash into the moon; most will form an orbiting cloud that gets smaller and tighter.
I tossed the trigonometric stuff and reverted back to degrees instead of radians. I use vector addition logic to work out the motion.
In the end, it's just a crude model.
By changing 180 to some other offsets, for example 195, in the def follow() in cdlane's code,
meteor.setheading(195 + meteor.towards(x, y))
then the metors would not go straight (180 degree) towards the Deimos, but instead would show some spiral movement towards the center.
Great example provided!

Getting dots to follow bigger dot

So this is a program I made for some dots to be attracted to a bigger dot and for that bigger dot to grow. The issue I'm facing right now is that the dots don't follow the bigger dot but rather seem to move away from it. The way I'm getting it to get closer is by translating the points, one to (0,0), the other to [t2.xcor() - t1.xcor() , t2.ycor()- t1.ycor()] , and then finding C with the Pythagorean theorem, and then using arc cosine to find the angle it needs to face in order to move towards the bigger dot.
from turtle import *
import sys
from math import *
#grows t1 shape + has it follow cursor
def grow(x, y):
t1.ondrag(None)
t1.goto(x,y)
global big, nig
t1.shapesize(big,nig)
big += .004
nig += .004
t1.ondrag(grow)
follow()
#has create()'d dots follow t1
def follow():
global count
#t1.ondrag(None)
screen.tracer(0,0)
for p in lx:
#print(lx[0:5])
t2.goto(p, ly[count])
t2.dot(4, "white")
if ly[count] != 0:
yb = abs(t2.ycor() - t1.ycor())
xb = abs((t2.xcor() - t1.xcor()))
c = sqrt((xb**2 + yb**2))
#print(y,x,c)
#print(lx)
t2.seth(360 - degrees(acos(yb/c)))
else:
t2.seth(0)
t2.forward(20)
t2.dot(4, "purple")
lx.pop(count)
ly.pop(count)
lx.insert(count, t2.xcor())
ly.insert(count, t2.ycor())
count += 1
#print(lx[0:5])
#screen.update()
screen.tracer(1,10)
count = 0
#t1.ondrag(follow)
#quits program
def quit():
screen.bye()
sys.exit(0)
#create()'s dots with t2
def create():
screen.tracer(0,0)
global nux, nuy, count3
while nuy > -400:
t2.goto(nux, nuy)
if t2.pos() != t1.pos():
t2.dot(4, "purple")
lx.append(t2.xcor())
ly.append(t2.ycor())
nux += 50
count3 += 1
if count3 == 17:
nuy = nuy - 50
nux = -400
count3 = 0
screen.tracer(1, 10)
#variables
count3 = count = 0
big = nig = .02
lx = []
ly = []
nux = -400
nuy = 300
screen = Screen()
screen.screensize(4000,4000)
t2 = Turtle()
t2.ht()
t2.pu()
t2.speed(0)
t2.shape("turtle")
t1 = Turtle()
t1.shape("circle")
t1.penup()
t1.speed(0)
t1.color("purple")
t1.shapesize(.2, .2)
create()
screen.listen()
screen.onkeypress(quit, "Escape")
t1.ondrag(grow)
#t1.ondrag(follow)
#screen.update()
screen.mainloop()
I see two (similar) issues with your code. First, you can toss the fancy math as you're reinventing turtle's .towards() method which gives you the angle you seek. Second, you're reinventing stamps which, unlike most turtle elements, can be cleared cleanly off the screen via clearstamp(). Also, you're using parallel arrays of coordinates which indicates lack of a proper data structure. I've replaced this with a single array containing tuples of positions and stamps.
I've adjusted the dynamics of your program, making the dots act independently (on a timer) and not rely on the movement of the cursor. I.e. they move towards the cursor whether it's moving or not. Also, I've made the cursor only grow when a dot reaches it and disappears:
from turtle import Turtle, Screen
CURSOR_SIZE = 20
def move(x, y):
""" has it follow cursor """
t1.ondrag(None)
t1.goto(x, y)
screen.update()
t1.ondrag(move)
def grow():
""" grows t1 shape """
global t1_size
t1_size += 0.4
t1.shapesize(t1_size / CURSOR_SIZE)
screen.update()
def follow():
""" has create()'d dots follow t1 """
global circles
new_circles = []
for (x, y), stamp in circles:
t2.clearstamp(stamp)
t2.goto(x, y)
t2.setheading(t2.towards(t1))
t2.forward(2)
if t2.distance(t1) > t1_size // 2:
new_circles.append((t2.position(), t2.stamp()))
else:
grow() # we ate one, make t1 fatter
screen.update()
circles = new_circles
if circles:
screen.ontimer(follow, 50)
def create():
""" create()'s dots with t2 """
count = 0
nux, nuy = -400, 300
while nuy > -400:
t2.goto(nux, nuy)
if t2.distance(t1) > t1_size // 2:
circles.append((t2.position(), t2.stamp()))
nux += 50
count += 1
if count == 17:
nuy -= 50
nux = -400
count = 0
screen.update()
# variables
t1_size = 4
circles = []
screen = Screen()
screen.screensize(900, 900)
t2 = Turtle('circle', visible=False)
t2.shapesize(4 / CURSOR_SIZE)
t2.speed('fastest')
t2.color('purple')
t2.penup()
t1 = Turtle('circle')
t1.shapesize(t1_size / CURSOR_SIZE)
t1.speed('fastest')
t1.color('orange')
t1.penup()
t1.ondrag(move)
screen.tracer(False)
create()
follow()
screen.mainloop()
You should be able to rework this code to do whatever it is you want. I strongly recommend you spend some time reading the Turtle documentation so you don't need to reinvent its many features.

Categories

Resources