the following code shows a sylvester fountain:
with a black and blue background.
i want some colorful rockets and rocketlauncher.
the following code shows all details with the physics colors and shows how the fountain is programmed.
iam thankful for help.
import tkinter as tk
from time import time
from random import choice, uniform
from math import sin, cos
from dataclasses import dataclass
## Basic Geometric Shapes ######################################################
@dataclass
class TkShape:
"""Common base class for geometric shapes like circles, squares and
triangles, which can be drawn on the `tk.Canvas` of a window.
"""
def add_to_canvas(self, cv: tk.Canvas):
"""Add this shape to a canvas. Specific shapes, which inherit from
TkShape, are expected to override this method. If we add a shape to a
canvas, the Tk library gives us an id, which can be used to remove the
shape again. We store this id in the `cid` attribute, such that the
`remove_from_canvas` method can remove it again.
"""
pass
def remove_from_canvas(self, cv: tk.Canvas):
"""Delete this shape from the canvas. Works the same for any shape."""
if self.cid != None:
cv.delete(self.cid)
self.cid = None
@dataclass
class Circle(TkShape):
"""Class for a circle shape.
The geometry and appearance of the circle is determined by its position on
the canvas, its radius and its color.
Attributes:
pos_x: The x-coordinate of the circle's center on the canvas.
pos_y: The y-coordinate of the circle's center on the canvas.
radius: The radius of the circle.
color: The color of the circle.
"""
pos_x: float
pos_y: float
radius: float
color: str
def add_to_canvas(self, cv: tk.Canvas):
"""Draw the circle on the canvas."""
self.cid = cv.create_oval(
self.pos_x - self.radius,
self.pos_y - self.radius,
self.pos_x + self.radius,
self.pos_y + self.radius,
fill = self.color)
@dataclass
class Square(TkShape):
"""Class for a square shape.
The geometry and appearance of the square is determined by its position
of it's center on the canvas, its size and its color.
Attributes:
pos_x: The x-coordinate of the square's center on the canvas.
pos_y: The y-coordinate of the square's center on the canvas.
size: The size of the square.
color: The color of the square.
"""
pos_x: float
pos_y: float
size: float
color: str
def add_to_canvas(self, cv: tk.Canvas):
"""Draw the square on the canvas."""
self.cid = cv.create_rectangle(
self.pos_x - self.size,
self.pos_y - self.size,
self.pos_x + self.size,
self.pos_y + self.size,
fill = self.color)
@dataclass
class Triangle(TkShape):
"""Class for a triangle shape.
The geometry and appearance of the circle is determined by the position of
its corners on the canvas and its color.
Attributes:
pos_x1: The x-coordinate of the object's first corner.
pos_y1: The y-coordinate of the object's first corner.
pos_x2: The x-coordinate of the object's second corner.
pos_y2: The y-coordinate of the object's second corner.
pos_x3: The x-coordinate of the object's third corner.
pos_y3: The y-coordinate of the object's third corner.
color: The color of the circle.
"""
pos_x1: float
pos_y1: float
pos_x2: float
pos_y2: float
pos_x3: float
pos_y3: float
color: str
def add_to_canvas(self, cv: tk.Canvas):
"""Draw the triangle on the canvas."""
self.cid = cv.create_polygon(
self.pos_x1, self.pos_y1,
self.pos_x2, self.pos_y2,
self.pos_x3, self.pos_y3,
fill = self.color)
## Physics #####################################################################
@dataclass
class PhysicsObject:
"""Common base class for objects, which are influenced by physics and can be
drawn on a screen.
Attributes:
pos_x: The x-coordinate of the object's position on the surface.
pos_y: The y-coordinate of the object's position on the surface.
vel_x: How fast the object moves horizontally.
vel_y: How fast the object moves vertically.
"""
pos_x: float
pos_y: float
vel_x: float
vel_y: float
def update(self, dt: float, gravity: float):
"""Simulate what physically happens to the object in the next `dt`
seconds. The object's position is changed according to its velocity,
which in turn is changed according to an external `gravity` constant."""
self.pos_x += dt * self.vel_x
self.pos_y += dt * self.vel_y
self.vel_y += dt * gravity
def render(self) -> list[TkShape]:
"""Describes how and where to draw this object by returning a list of
shapes. Subclasses are expected to override this method."""
return []
def is_alive(self) -> bool:
"""Check if the object ist still alive. Overwritten by derived
classes. PhysicsObjects which are dead will be removed from the
simulation for performance reasons."""
return True
## Particles ###################################################################
@dataclass
class Particle(PhysicsObject):
"""Common base class for fireworks particles, e.g. sparks flying off a
rocket or coming out of a volcano.
Any Particle is also a PhysicsObject, since they fly around and can be seen,
but they also have a life time, after which they burn out, and a certain
color and size. The precise shape of a Particle is determined by its
subclasses below.
Arguments:
remaining_time: How many seconds until this particle is burnt out.
color: The color of the particle.
size: The size of the particle.
"""
remaining_time: float
color: str
size: float
def update(self, dt: float, gravity: float):
"""A particle is influenced by physics, just like any other
PhysicsObject, but additionally the passed time `dt` influences the
`remaining_time` before the particle burns out."""
super().update(dt, gravity)
self.remaining_time -= dt
def is_alive(self):
"""Check if the particle is still alive or already burned out."""
return self.remaining_time >= 0.0
@dataclass
class CircularParticle(Particle):
"""A Particle which has the shape of a Circle."""
def render(self) -> list[TkShape]:
"""Draw the particle as a single circle shape."""
return [
Circle(
pos_x = self.pos_x,
pos_y = self.pos_y,
radius = self.size,
color = self.color
)
]
@dataclass
class SquareParticle(Particle):
"""A Particle which has the shape of a Square."""
def render(self) -> list[TkShape]:
"""Draw the particle as a single square shape."""
return [
Square(
pos_x = self.pos_x,
pos_y = self.pos_y,
size = self.size,
color = self.color
)
]
@dataclass
class TriangleParticle(Particle):
"""A Particle which has the shape of a Triangle."""
def render(self) -> list[TkShape]:
"""Draw the particle as a single triangle shape."""
return [
Triangle(
pos_x1 = self.pos_x - self.size,
pos_y1 = self.pos_y - self.size,
pos_x2 = self.pos_x + self.size,
pos_y2 = self.pos_y - self.size,
pos_x3 = self.pos_x,
pos_y3 = self.pos_y + self.size,
color = self.color
)
]
## Fireworks ###################################################################
@dataclass
class Firework(PhysicsObject):
"""Common base class for a Firework maintaining a list of Particles.
Classes inheriting from a Firework, like our Volcano, are expected to
override the `update` method to dynamically spawn new particles.
Attributes:
particles: list of the currently burning Particles, which belong to this
firework. At the beginning this should usually be the empty
list, because fireworks tend to spawn their own particles.
"""
particles: list[Particle]
def update_particles(self, dt: float, gravity: float):
# Update all particles.
for p in self.particles:
p.update(dt, gravity)
# Remove dead particles.
alive_particles = []
for p in self.particles:
if p.is_alive():
alive_particles += [p]
self.particles = alive_particles
def update(self, dt: float, gravity: float):
# Simulate the physics for the fireworks object itself.
super().update(dt, gravity)
# Simulate the physics for all its particles.
self.update_particles(dt, gravity)
def render(self) -> list[TkShape]:
"""A Firework is rendered by rendering all its particles. Subclasses may
want to override this method to render additional shapes for the
firework body."""
shapes = []
for p in self.particles:
shapes += p.render()
return shapes
@dataclass
class Volcano(Firework):
"""A Volcano is a Firework, which continuously emits colored particles and
has a body that looks like a large triangle.
Attributes:
particles_per_second: the number of particles to spawn per second.
colors: the colors of the particles to spawn. Since our Volcano is
pretty fancy, it spawns particles of different colors as time passes.
age: how many seconds the Volcano exists.
_particles_to_spawn: how many fractions of a particle we still need to
spawn. This is an implementation detail from the physics simulation:
if the time steps in `update` are very small, then it could be the
case, that in each time step we would need to spawn 0.001 particles,
so we need to accumulate those amounts, such that we notice when it
goes beyond 1.0 and we actually have to spawn a new particle.
"""
particles_per_second: float = 100.0
colors: tuple[str] = ("red", "green", "silver", "gold")
age: float = 0.0
_particles_to_spawn: float = 0.0
def render(self) -> list[TkShape]:
"""A Volcano is drawn as a big triangle with particles coming out of
th