using turtle to make animation - python

I am simulating an animation using turtle package in python.
The effect I want to achieve is like the .gif link I attached here: https://i.stack.imgur.com/prCyQ.gif
(There are 200 concentric shells in my circle. The circle's radius changes over time with my own imported data set, and each shell's radius changes proportionally, each shell has a slight different bluish color but each will also change over time based on my dataset.)
Here is my code generated.
import csv
from turtle import *
def colorConvert(digit:float):
if digit == 10:
digit = 'A'
elif digit == 11:
digit = 'B'
elif digit == 12:
digit = 'C'
elif digit == 13:
digit = 'D'
elif digit == 14:
digit = 'E'
elif digit == 15:
digit = 'F'
return digit
bgcolor("black")
radius=[]
with open('D:/SURA/Archive/water-loss/output-sorption.csv') as loss_radius:
reader = csv.reader(loss_radius,delimiter=',')
for row in reader:
radius.append(float(row[1]))
conc = [] # convert to float when accessing elements
with open('D:/SURA/Archive/output-water-concentration.csv') as water:
reader = csv.reader(water, delimiter=',')
conc = [list(map(float,row)) for row in reader]
for i in range(301):
for j in range(200):
conc[i][j] -= 140
shell_radius=[[0]*200]*301
max=200
radius_max=radius[0]
for i in range(0,301,30):
for j in range(0,200,20):
radius_max = radius[i]
shell_radius[i][j] = radius_max * ((max - j) / max)
digit5 = int(float(conc[i][j]) // 16)
digit6 = int(((float(conc[i][j]) / 16) - int(float(conc[i][j]) / 16)) * 16)
color_set = '#0000'+str(colorConvert(digit5))+str(colorConvert(digit6))
up()
goto(0,-shell_radius[i][j]*0.05)
down()
pencolor(color_set)
fillcolor(color_set)
begin_fill()
circle(shell_radius[i][j]*0.05)
end_fill()
exitonclick()
(the last nested for should run from 0 to 301 and 0 to 200, I shorten them for saving time to show the final visual effect~)
It works, but what I want to optimize is that after one graphing outer for loop finishes executing, the graph can disappear, and starts the next out for loop. Also, is there any way to only show the final graph after each outer for loop execution finishes? So that the demo can be the same as my gif(there is no drawing process displaying).
Thanks in advance!!

You can change turtle's speed while drawing (turtle speed), makes the animation slow at the end of each frame.
Also you can stop for a while using Python's sleep() function, between each frame.
I guess you can instant draw with the fastest speed, and use tracer(False) and hideturtle() to hide it.

Related

Python turtle: How can I make the turtle go to a point in a list of points after it goes through the list

For example, point 1 connects to point 9, then point 2 connects to point 10 and so on for len(pointList).
I am currently stuck because once the turtle attempts to access the 32nd point, it gets an out of bounds error because the point it would need to connect to is point 0 but it wont do so with my current code.
Each point will need to connect to the 9th point ahead of it for the length of the list (40 in this case because of the text file line amount)
I am hinted to use % operator, but I do not know how I would use it.
code
from turtle import *
speed(10)
fileName = input("What is the name of the file? ")
file = open(fileName, 'r', encoding='UTF-8')
pointList = []
penup()
for line in file:
lineTokens = line.split()
x = float(lineTokens[0])
y = float(lineTokens[1])
point = (x,y)
pointList.append(point)
def drawPoints():
for point in pointList:
goto(point)
dot(5, "black")
drawPoints()
i = 0
for point in range(len(pointList)):
print(point)
pencolor("red")
goto(pointList[i])
down()
goto(pointList[i + 9])
up()
i += 1
Text file I am reading from to collect points (if needed)
31.286893008046174 -197.53766811902756
61.80339887498949 -190.21130325903073
90.79809994790936 -178.20130483767358
117.55705045849463 -161.80339887498948
141.4213562373095 -141.4213562373095
161.80339887498948 -117.55705045849463
178.20130483767355 -90.79809994790936
190.21130325903067 -61.803398874989504
197.5376681190275 -31.286893008046214
199.99999999999994 -6.394884621840902e-14
197.5376681190275 31.286893008046086
190.21130325903067 61.80339887498938
178.20130483767352 90.79809994790924
161.80339887498945 117.55705045849447
141.42135623730948 141.42135623730934
117.55705045849464 161.8033988749893
90.7980999479094 178.20130483767338
61.80339887498957 190.2113032590305
31.28689300804629 197.5376681190273
1.5987211554602254e-13 199.99999999999974
-31.286893008045972 197.5376681190273
-61.80339887498924 190.21130325903047
-90.79809994790908 178.20130483767332
-117.55705045849432 161.80339887498926
-141.42135623730914 141.42135623730928
-161.80339887498906 117.55705045849444
-178.20130483767312 90.79809994790921
-190.21130325903022 61.80339887498938
-197.53766811902702 31.286893008046114
-199.99999999999946 -5.329070518200751e-15
-197.53766811902702 -31.286893008046125
-190.2113032590302 -61.803398874989384
-178.20130483767304 -90.7980999479092
-161.80339887498897 -117.5570504584944
-141.421356237309 -141.42135623730923
-117.55705045849416 -161.80339887498914
-90.79809994790895 -178.20130483767318
-61.80339887498913 -190.21130325903027
-31.286893008045876 -197.53766811902707
2.327027459614328e-13 -199.99999999999952
You can use the following code:
goto(pointList[(i + 9)%len(pointList)])
What this does is get a zero if the expression i + 9 evaluates to len(pointList)
Another less elegant solution is:
if i + 9 < len(pointList):
goto(pointList[i + 9])
else:
goto(pointList[0])

How to properly add gradually increasing/decreasing space between objects?

I've trying to implement transition from an amount of space to another which is similar to acceleration and deceleration, except i failed and the only thing that i got from this was this infinite stack of mess, here is a screenshot showing this in action:
you can see a very black circle here, which are in reality something like 100 or 200 circles stacked on top of each other
and i reached this result using this piece of code:
def Place_circles(curve, circle_space, cs, draw=True, screen=None):
curve_acceleration = []
if type(curve) == tuple:
curve_acceleration = curve[1][0]
curve_intensity = curve[1][1]
curve = curve[0]
#print(curve_intensity)
#print(curve_acceleration)
Circle_list = []
idx = [0,0]
for c in reversed(range(0,len(curve))):
for p in reversed(range(0,len(curve[c]))):
user_dist = circle_space[curve_intensity[c]] + curve_acceleration[c] * p
dist = math.sqrt(math.pow(curve[c][p][0] - curve[idx[0]][idx[1]][0],2)+math.pow(curve [c][p][1] - curve[idx[0]][idx[1]][1],2))
if dist > user_dist:
idx = [c,p]
Circle_list.append(circles.circles(round(curve[c][p][0]), round(curve[c][p][1]), cs, draw, screen))
This place circles depending on the intensity (a number between 0 and 2, random) of the current curve, which equal to an amount of space (let's say between 20 and 30 here, 20 being index 0, 30 being index 2 and a number between these 2 being index 1).
This create the stack you see above and isn't what i want, i also came to the conclusion that i cannot use acceleration since the amount of time to move between 2 points depend on the amount of circles i need to click on, knowing that there are multiple circles between each points, but not being able to determine how many lead to me being unable to the the classic acceleration formula.
So I'm running out of options here and ideas on how to transition from an amount of space to another.
any idea?
PS: i scrapped the idea above and switched back to my master branch but the code for this is still available in the branch i created here https://github.com/Mrcubix/Osu-StreamGenerator/tree/acceleration .
So now I'm back with my normal code that don't possess acceleration or deceleration.
TL:DR i can't use acceleration since i don't know the amount of circles that are going to be placed between the 2 points and make the time of travel vary (i need for exemple to click circles at 180 bpm of one circle every 0.333s) so I'm looking for another way to generate gradually changing space.
First, i took my function that was generating the intensity for each curves in [0 ; 2]
Then i scrapped the acceleration formula as it's unusable.
Now i'm using a basic algorithm to determine the maximum amount of circles i can place on a curve.
Now the way my script work is the following:
i first generate a stream (multiple circles that need to be clicked at high bpm)
this way i obtain the length of each curves (or segments) of the polyline.
i generate an intensity for each curve using the following function:
def generate_intensity(Circle_list: list = None, circle_space: int = None, Args: list = None):
curve_intensity = []
if not Args or Args[0] == "NewProfile":
prompt = True
while prompt:
max_duration_intensity = input("Choose the maximum amount of curve the change in intensity will occur for: ")
if max_duration_intensity.isdigit():
max_duration_intensity = int(max_duration_intensity)
prompt = False
prompt = True
while prompt:
intensity_change_odds = input("Choose the odds of occurence for changes in intensity (1-100): ")
if intensity_change_odds.isdigit():
intensity_change_odds = int(intensity_change_odds)
if 0 < intensity_change_odds <= 100:
prompt = False
prompt = True
while prompt:
min_intensity = input("Choose the lowest amount of spacing a circle will have: ")
if min_intensity.isdigit():
min_intensity = float(min_intensity)
if min_intensity < circle_space:
prompt = False
prompt = True
while prompt:
max_intensity = input("Choose the highest amount of spacing a circle will have: ")
if max_intensity.isdigit():
max_intensity = float(max_intensity)
if max_intensity > circle_space:
prompt = False
prompt = True
if Args:
if Args[0] == "NewProfile":
return [max_duration_intensity, intensity_change_odds, min_intensity, max_intensity]
elif Args[0] == "GenMap":
max_duration_intensity = Args[1]
intensity_change_odds = Args[2]
min_intensity = Args[3]
max_intensity = Args[4]
circle_space = ([min_intensity, circle_space, max_intensity] if not Args else [Args[0][3],circle_space,Args[0][4]])
count = 0
for idx, i in enumerate(Circle_list):
if idx == len(Circle_list) - 1:
if random.randint(0,100) < intensity_change_odds:
if random.randint(0,100) > 50:
curve_intensity.append(2)
else:
curve_intensity.append(0)
else:
curve_intensity.append(1)
if random.randint(0,100) < intensity_change_odds:
if random.randint(0,100) > 50:
curve_intensity.append(2)
count += 1
else:
curve_intensity.append(0)
count += 1
else:
if curve_intensity:
if curve_intensity[-1] == 2 and not count+1 > max_duration_intensity:
curve_intensity.append(2)
count += 1
continue
elif curve_intensity[-1] == 0 and not count+1 > max_duration_intensity:
curve_intensity.append(0)
count += 1
continue
elif count+1 > 2:
curve_intensity.append(1)
count = 0
continue
else:
curve_intensity.append(1)
else:
curve_intensity.append(1)
curve_intensity.reverse()
if curve_intensity.count(curve_intensity[0]) == len(curve_intensity):
print("Intensity didn't change")
return circle_space[1]
print("\n")
return [circle_space, curve_intensity]
with this, i obtain 2 list, one with the spacing i specified, and the second one is the list of randomly generated intensity.
from there i call another function taking into argument the polyline, the previously specified spacings and the generated intensity:
def acceleration_algorithm(polyline, circle_space, curve_intensity):
new_circle_spacing = []
for idx in range(len(polyline)): #repeat 4 times
spacing = []
Length = 0
best_spacing = 0
for p_idx in range(len(polyline[idx])-1): #repeat 1000 times / p_idx in [0 ; 1000]
# Create multiple list containing spacing going from circle_space[curve_intensity[idx-1]] to circle_space[curve_intensity[idx]]
spacing.append(np.linspace(circle_space[curve_intensity[idx]],circle_space[curve_intensity[idx+1]], p_idx).tolist())
# Sum distance to find length of curve
Length += abs(math.sqrt((polyline[idx][p_idx+1][0] - polyline[idx][p_idx][0]) ** 2 + (polyline [idx][p_idx+1][1] - polyline[idx][p_idx][1]) ** 2))
for s in range(len(spacing)): # probably has 1000 list in 1 list
length_left = Length # Make sure to reset length for each iteration
for dist in spacing[s]: # substract the specified int in spacing[s]
length_left -= dist
if length_left > 0:
best_spacing = s
else: # Since length < 0, use previous working index (best_spacing), could also jsut do `s-1`
if spacing[best_spacing] == []:
new_circle_spacing.append([circle_space[1]])
continue
new_circle_spacing.append(spacing[best_spacing])
break
return new_circle_spacing
with this, i obtain a list with the space between each circles that are going to be placed,
from there, i can Call Place_circles() again, and obtain the new stream:
def Place_circles(polyline, circle_space, cs, DoDrawCircle=True, surface=None):
Circle_list = []
curve = []
next_circle_space = None
dist = 0
for c in reversed(range(0, len(polyline))):
curve = []
if type(circle_space) == list:
iter_circle_space = iter(circle_space[c])
next_circle_space = next(iter_circle_space, circle_space[c][-1])
for p in reversed(range(len(polyline[c])-1)):
dist += math.sqrt((polyline[c][p+1][0] - polyline[c][p][0]) ** 2 + (polyline [c][p+1][1] - polyline[c][p][1]) ** 2)
if dist > (circle_space if type(circle_space) == int else next_circle_space):
dist = 0
curve.append(circles.circles(round(polyline[c][p][0]), round(polyline[c][p][1]), cs, DoDrawCircle, surface))
if type(circle_space) == list:
next_circle_space = next(iter_circle_space, circle_space[c][-1])
Circle_list.append(curve)
return Circle_list
the result is a stream with varying space between circles (so accelerating or decelerating), the only issue left to be fixed is pygame not updating the screen with the new set of circle after i call Place_circles(), but that's an issue i'm either going to try to fix myself or ask in another post
the final code for this feature can be found on my repo : https://github.com/Mrcubix/Osu-StreamGenerator/tree/Acceleration_v02

Trying to properly iterate through pygame sprite group

So I am building a simple infinite run game for school and I am stuck on trying to spawn the obstacles. I am trying to check each obstacle sprite and if it has gone off the screen (the background and obstacles move from right to left). If it has gone off screen, I want to remove the sprite and set up another one on the right side of the screen. But every time an obstacle goes off the left side of the screen, an infinite amount of obstacles start spawning. I am new to pygame and python in general. Any help would be greatly appreciated. Thanks in advance.
def obstacle_off_screen(self):
numDeleted = 0
for cur_sprite in self.all_objects_list:
print("first loop")
if cur_sprite.rect.x < 0:
print("second")
cur_sprite.kill
numDeleted += 1
while numDeleted != 0:
print("third")
self.add_obstacle()
numDeleted -= 1
def add_obstacle(self):
#add parameters
if self.get_Speed() == 15:
x = 1000
y = 400
elif self.get_Speed() == 20:
x = 1000
y = 400
elif self.get_Speed() == 25:
x = 1000
y = 400
elif self.get_Speed() == 30:
x = 1000
y = 400
self.all_objects_list.add(Obstacle('src/paw.gif', [x, y]))
For now, I only have one obstacle that I initially spawn
cur_sprite.kill is a function, so when you want to call it use (), like cur_sprite.kill().
That's your problem. The obstacles out of screen don't get removed from their sprite groups.

Spawning objects in groups when the first object of the group was spawned randomly Python

I'm currently doing a project, and in the code I have, I'm trying to get trees .*. and mountains .^. to spawn in groups around the first tree or mountain which is spawned randomly, however, I can't figure out how to get the trees and mountains to spawn in groups around a single randomly generated point. Any help?
grid = []
def draw_board():
row = 0
for i in range(0,625):
if grid[i] == 1:
print("..."),
elif grid[i] == 2:
print("..."),
elif grid[i] == 3:
print(".*."),
elif grid[i] == 4:
print(".^."),
elif grid[i] == 5:
print("[T]"),
else:
print("ERR"),
row = row + 1
if row == 25:
print ("\n")
row = 0
return
There's a number of ways you can do it.
Firstly, you can just simulate the groups directly, i.e. pick a range on the grid and fill it with a specific figure.
def generate_grid(size):
grid = [0] * size
right = 0
while right < size:
left = right
repeat = min(random.randint(1, 5), size - right) # *
right = left + repeat
grid[left:right] = [random.choice(figures)] * repeat
return grid
Note that the group size need not to be uniformly distributed, you can use any convenient distribution, e.g. Poisson.
Secondly, you can use a Markov Chain. In this case group lengths will implicitly follow a Geometric distribution. Here's the code:
def transition_matrix(A):
"""Ensures that each row of transition matrix sums to 1."""
copy = []
for i, row in enumerate(A):
total = sum(row)
copy.append([item / total for item in row])
return copy
def generate_grid(size):
# Transition matrix ``A`` defines the probability of
# changing from figure i to figure j for each pair
# of figures i and j. The grouping effect can be
# obtained by setting diagonal entries A[i][i] to
# larger values.
#
# You need to specify this manually.
A = transition_matrix([[5, 1],
[1, 5]]) # Assuming 2 figures.
grid = [random.choice(figures)]
for i in range(1, size):
current = grid[-1]
next = choice(figures, A[current])
grid.append(next)
return grid
Where the choice function is explained in this StackOverflow answer.

I have made a Pygame program but when the sprites overlap it has a white outline on some of them

I am making a level editor for a game I am working on. I have it set up so that you can put a picture in a folder and it will split it from a 90x90 pix block to 9 30x30 pix blocks that can be used in the editor. The code that makes the 30x30 blocks looks like this:
one = Image.new('RGBA',(30,30),(255,255,255,0))
two = Image.new('RGBA',(30,30),(255,255,255,0))
three = Image.new('RGBA',(30,30),(255,255,2550,0))
four = Image.new('RGBA',(30,30),(255,255,255,0))
five = Image.new('RGBA',(30,30),(255,255,255,0))
six = Image.new('RGBA',(30,30),(255,255,2550,0))
seven = Image.new('RGBA',(30,30),(255,255,2550,0))
eight = Image.new('RGBA',(30,30),(255,255,255,0))
nine = Image.new('RGBA',(30,30),(255,255,255,0))
for y in range(1,iy):
print ((y/9)*10),"%"
for x in range(1,ix):
pixel = im.getpixel((x,y))
if y<30:
if x<30:
one.putpixel((x,y),pixel)
elif x<60:
four.putpixel((x-30,y),pixel)
else:
seven.putpixel((x-60,y),pixel)
elif y <60:
if x <30:
two.putpixel((x,y-30),pixel)
elif x <60:
five.putpixel((x-30,y-30),pixel)
else:
eight.putpixel((x-60,y-30),pixel)
else:
if x < 30:
three.putpixel((x,y-60),pixel)
elif x < 60:
six.putpixel((x-30,y-60),pixel)
else:
nine.putpixel((x-60,y-60),pixel)
add = [im,one,two,three,four,five,six,seven,eight,nine]
bdict[s]= add
cd = os.getcwd()+'\\'+s +'\\'
one.save(cd + s +'one.png')
two.save(cd+s+'two.png')
three.save(cd+s+'three.png')
four.save(cd+s+'four.png')
five.save(cd+s+'five.png')
six.save(cd+s+'six.png')
seven.save(cd+s+'seven.png')
eight.save(cd+s+'eight.png')
nine.save(cd+s+'nine.png')
im.save(cd + fol)
im = os.getcwd()+'\\'+fol
one = os.getcwd()+'\\'+s+'one.png'
two =os.getcwd()+'\\'+s+'two.png'
three =os.getcwd()+'\\'+s+'three.png'
four = os.getcwd()+'\\'+s+'four.png'
five =os.getcwd()+'\\'+s+'five.png'
six =os.getcwd()+'\\'+s+'six.png'
seven =os.getcwd()+'\\'+s+'seven.png'
eight =os.getcwd()+'\\'+s+'eight.png'
nine =os.getcwd()+'\\'+s+'nine.png'
add = [im,one,two,three,four,five,six,seven,eight,nine]
bdict[s]= add
maxes[0]+=1
when two blocks overlap, they have this strange white bar that only shows up around the top left and right blocks and the bottom left and right blocks. That looks like this:
The only other place that might have the error is where the code loads the sprites, here:
grounds is an array containing the different rectangles while gsprites is an array containing the different sprites in the format that they are loaded by Pygame.
for number in range(0,len(grounds)):
cr = grounds[number]
cr.left += xscroll
cr.top += yscroll
pygame.draw.rect(windowsurface,clear,cr)
change = pygame.transform.scale(gsprites[number],(30,30))
windowsurface.blit(gsprites[number],cr)
cr.left -= xscroll
cr.top -= yscroll
I am running windows vista 32 bit.
Never mind, I figured it out. I was drawing a rect before I blitted the sprite, and it ended up showing up under the sprite.

Categories