pythonanimationrecursionpython-3.xquickdraw

python3 recursion animation in QuickDraw


I have a text file which has the planets and their corresponding moons/satellites along with their orbital radius and period and I would like to use this to create an animation in quickdraw similar to the one below:

solar system animation

The text file is as followed:

RootObject: Sun

Object: Sun
Satellites: Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Ceres,Pluto,Haumea,Makemake,Eris
Radius: 20890260
Orbital Radius: 0

Object: Miranda
Orbital Radius: 5822550
Radius: 23500
Period: 1.413

Object: Ariel
Orbital Radius: 8595000
Radius: 60000
Period: 2.520379

Object: Umbriel
Orbital Radius: 11983500
Radius: 60000
Period: 4.144177

Object: Titania
Orbital Radius: 19575000
Radius: 75000
Period: 8.7058

Object: Oberon
Orbital Radius: 26235000
Radius: 75000
Period: 13.463

Object: Uranus
Orbital Radius: 453572956
Radius: 2555900
Period: 30799
Satellites: Puck,Miranda,Ariel,Umbriel,Titania,Oberon

Object: Neptune
Orbital Radius: 550000000
Radius: 2476400
Period: 60190
Satellites: Triton

Object: Triton
Orbital Radius: 40000000
Radius: 135300
Period: -5.8

Object: Mercury
Orbital Radius: 38001200
Period: 87.9691
Radius: 243900.7

Object: Venus
Orbital Radius: 57477000
Period: 224.698
Radius: 605100.8

Object: Earth
Orbital Radius: 77098290
Period: 365.256363004
Radius: 637100.0
Satellites: Moon

Object: Moon
Orbital Radius: 18128500
Radius: 173700.10
Period: 27.321582

Object: Mars
Orbital Radius: 106669000
Period: 686.971
Radius: 339600.2
Satellites: Phobos,Deimos

Object: Phobos
Orbital Radius: 3623500.6
Radius: 200000
Period: 0.31891023

Object: Deimos
Orbital Radius: 8346000
Period: 1.26244
Radius: 200000.2

Object: Jupiter
Orbital Radius: 210573600
Period: 4332.59
Radius: 7149200
Satellites: Io,Europa,Ganymede,Callisto

Object: Ceres
Orbital Radius: 130995855
Period: 1679.67
Radius: 48700

Object: Io
Orbital Radius: 22000000
Period: 1.7691377186
Radius: 182100.3

Object: Europa
Orbital Radius: 36486200
Period: 3.551181
Radius: 156000.8

Object: Ganymede
Orbital Radius: 47160000
Period: 7.15455296
Radius: 263400

Object: Callisto
Orbital Radius: 69700000
Period: 16.6890184
Radius: 241000

Object: Saturn
Orbital Radius: 353572956
Period: 10759.22
Radius: 6026800
Satellites: Mimas,Enceladus,Tethys,Dione,Rhea,Titan,Iapetus

Object: Mimas
Orbital Radius: 8433396
Radius: 20600
Period: 0.9

Object: Enceladus
Orbital Radius: 10706000
Radius: 25000
Period: 1.4

Object: Tethys
Orbital Radius: 13706000
Radius: 50000
Period: 1.9

Object: Dione
Orbital Radius: 17106000
Radius: 56000
Period: 2.7

Object: Rhea
Orbital Radius: 24000000
Radius: 75000
Period: 4.5

Object: Titan
Orbital Radius: 50706000
Radius: 257600
Period: 15.945

Object: Iapetus
Radius: 75000
Orbital Radius: 72285891
Period: 79

I've changed my original code (which was ridiculously longer) to this shorter one: (credit to sudo_O for all his awesome help)

file = open("data1.txt","r")

def data(file):
    d = {}
    for line in file:
        if line.strip() != '':
            key,value = line.split(":")
            if key == 'RootObject':
                continue
            if key == 'Object':                
                obj = value.strip()
                d[obj]={}
            else:
                d[obj][key] = value.strip()
    return d

planets = data(file)

print(planets)

My biggest problem is that I don't know how to write code that imports the data from the file and utilizes it through recursion to create an animation similar to the one shown. I keep getting told that the solution is quite simple and the code is actually surprisingly short but I have no idea how to do it and it's actually very frustrating.

Here is my code for a circle orbiting a circle but it seems too complicated...

import math

print("circle",400,300,50)

print("flush false")

centreX=400
centreY=300
radius=100
angle=math.pi/2

while True:
    print("color 0 0 0")
    print("clear")
    print("color 255 0 255")
    print("circle",centreX,centreY,radius)
    childX=math.sin(angle)*radius*1.5+centreX
    childY=math.cos(angle)*radius*1.5+centreY
    print("circle",childX,childY,radius/2)
    print("refresh")
    angle+=0.01

code for scale:

scale=250/max([dict[planets]["Orbital Radius"] for x in dict if "Orbital Radius" in dict[planets]])

Code for orbiting planets:

print("flush false")
scale=250/max([dict[planets]["Orbital Radius"] for x in dict if "Orbital Radius" in dict[planets]])
t=0
x=400
y=300
while True:
    print("refresh")
    print("colour 0 0 0")
    print("clear")
    print("colour 255 255 255")
    print("fillcircle",x,y,dict['Sun']['Radius']*scale)
    print("text ", "\"Sun\"",x+dict['Sun']['Radius']*scale,y)
    r_earth=dict['Earth']['Orbital Radius']*scale;
    print("circle",x,y,r_earth)
    r_X=x+math.sin(t*2*math.pi/dict['Earth']['Period'])*r_earth
    r_Y=y+math.cos(t*2*math.pi/dict['Earth']['Period'])*r_earth
    print("fillcircle",r_X,r_Y,dict['Earth']['Radius']*scale)
    print("text ", "\"Earth\"",r_X+dict['Earth']['Radius']*scale,r_Y)
    t+=0.02

this code does not work very well however and as a beginner, I am at my limits for this problem...

last ditch effort !! would this could work?

print("flush false")
scale = 250/max([dict[planets]["Orbital Radius"] for x in dict if "Orbital Radius" in dict[planets]])
t = 0
x = 400
y = 300
print("fillcircle",400,300,dict['Sun']['Radius']*scale)
print("text ", "\"Sun\"",x+dict['Sun']['Radius']*scale,y)


while True:
    r_new = dict['Object']['Orbital Radius']*scale
    print("circle",x,y,r_new)
    r_X = x + math.sin(t*2*math.pi/dict['Object']['Period'])*r_new
    r_Y = y + math.cos(t*2*math.pi/dict['Object']['Period'])*r_new
    print("fillcircle",r_X,r_Y,dict['Object']['Radius']*scale)
    print("text ",Object,r_X+dict['Object']['Radius']*scale,r_Y)
    t += 0.02
    if planets['Object']['Satellites'] = 0
        return
    else:
        r_sat = dict['Object']['Satellites']['Orbital Radius']*scale
        print("circle",x,y,r_sat)
        r_satX = x + math.sin(t*2*math.pi/dict['Object']['Satellites']['Period'])*r_sat
        r_satY = y + math.cos(t*2*math.pi/dict['Object']['Satellites']['Period'])*r_sat
        print("fillcircle",r_satX,r_satY,dict['Object']['Satellites']['Radius']*scale)
        print("text ",['Object']['Satellites'],r_satX+dict['Object']['Satellites']['Radius']*scale,r_satY)
        t += 0.02

Solution

  • First problem recursion - a recursive function is one that calls itself! A simple example of this is a countdown function such as:

    def countdown(n):
        # counting down the recursive way! 
        if n > 0:
            print n
            countdown(n-1)
        else:
            return
    

    Calling countdown(10) will print 10, 9, 8,.., 2, 1.

    You can see that countdown is passed a number n and all it does is prints that number and then calls itself but passing n-1 this time. Only once n=0 is passed will it not have anything left to do so each recursive call is returned. For your dictionary of dictionaries case (lets call it a library of dictionaries to avoid confusion) a recursive printing approach would be:

    1. Pass the whole library to the printing routine.
    2. Print the first dictionary.
    3. Recursively call itself and pass the library minus the first dictionary.
    4. Return when the library has no more dictionaries to print.

    Something like this:

    def recursive_print(dic):
    
        if len(dic) > 0:                  # If dictionaries in library > 0 
            print dic.keys()[0]           # Print the key i.e Earth
            print dic[dic.keys()[0]]      # Print the dictionary value for i.e Earth
            dic.popitem()                 # Remove the Earth dictionary from library
            recursive_print(dic)          # Recursive call 
        else:
            return                        # Printed all, return up the stack.
    
    
    planets = data(file)
    recursive_print(planets)
    

    The next step would be instead of printing the dictionaries in there current format you would do some calculations/conversion so the output is valid quickdraw input, like the code you have already for printing circles using quickdraw.

    You will need to worry about the scale, to ensure all objects fit on the drawing surface find the celestial body with the largest Orbital Radius and use that to calculate the scale.

    Using list comprehension similar to your code we can find the largest value:

    max([planet[key]['Orbital Radius'] for key in planet])
    
    >>> 8595000
    
    scale = gridsize/max([planet[key]['Orbital Radius'] for key in planet])