Related
This python code is made to show the animation of 3D solar system with 2d projection.I make two file named solar_system_3d.py and simple_solar_system.py to write the code.But it cannot show properly due to the error:
raceback (most recent call last):
File ~/simple_solar_system.py:14 in
sun = Sun(solar_system)
File ~/solar_system_3d.py:143 in init
super(Sun, self).init(solar_system, mass, position, velocity)
File ~/solar_system_3d.py:89 in init
self.velocity = Vector(*velocity)
TypeError: Vector() takes no arguments
Here is the code from "solar_system_3d.py":
updated version:
# solar_system_3d.py
import itertools
import math
import matplotlib.pyplot as plt
from vectors import Vector
class SolarSystem:
def __init__(self, size, projection_2d=False):
self.size = size
self.projection_2d = projection_2d
self.bodies = []
self.fig, self.ax = plt.subplots(
1,
1,
subplot_kw={"projection": "3d"},
figsize=(self.size / 50, self.size / 50),
)
self.fig.tight_layout()
if self.projection_2d:
self.ax.view_init(10, 0)
else:
self.ax.view_init(0, 0)
def add_body(self, body):
self.bodies.append(body)
def update_all(self):
self.bodies.sort(key=lambda item: item.position[0])
for body in self.bodies:
body.move()
body.draw()
def draw_all(self):
self.ax.set_xlim((-self.size / 2, self.size / 2))
self.ax.set_ylim((-self.size / 2, self.size / 2))
self.ax.set_zlim((-self.size / 2, self.size / 2))
if self.projection_2d:
self.ax.xaxis.set_ticklabels([])
self.ax.yaxis.set_ticklabels([])
self.ax.zaxis.set_ticklabels([])
else:
self.ax.axis(False)
plt.pause(0.001)
self.ax.clear()
def calculate_all_body_interactions(self):
bodies_copy = self.bodies.copy()
for idx, first in enumerate(bodies_copy):
for second in bodies_copy[idx + 1:]:
first.accelerate_due_to_gravity(second)
class SolarSystemBody:
min_display_size = 10
display_log_base = 1.3
def __init__(
self,
solar_system,
mass,
position=(0, 0, 0),
velocity=(0, 0, 0),
):
self.solar_system = solar_system
self.mass = mass
self.position = position
self.velocity = Vector(*velocity)
self.display_size = max(
math.log(self.mass, self.display_log_base),
self.min_display_size,
)
self.colour = "black"
self.solar_system.add_body(self)
def move(self):
self.position = (
self.position[0] + self.velocity[0],
self.position[1] + self.velocity[1],
self.position[2] + self.velocity[2],
)
def draw(self):
self.solar_system.ax.plot(
*self.position,
marker="o",
markersize=self.display_size + self.position[0] / 30,
color=self.colour
)
if self.solar_system.projection_2d:
self.solar_system.ax.plot(
self.position[0],
self.position[1],
-self.solar_system.size / 2,
marker="o",
markersize=self.display_size / 2,
color=(.5, .5, .5),
)
def accelerate_due_to_gravity(self, other):
distance = Vector(*other.position) - Vector(*self.position)
distance_mag = distance.get_magnitude()
force_mag = self.mass * other.mass / (distance_mag ** 2)
force = distance.normalize() * force_mag
reverse = 1
for body in self, other:
acceleration = force / body.mass
body.velocity += acceleration * reverse
reverse = -1
class Vector:
def __sub__(self, vec):
return Vector(self.x - vec.x)
class Sun(SolarSystemBody):
def __init__(
self,
solar_system,
mass=10_000,
position=(0, 0, 0),
velocity=(0, 0, 0),
):
super(Sun, self).__init__(solar_system, mass, position, velocity)
self.colour = "yellow"
class Planet(SolarSystemBody):
colours = itertools.cycle([(1, 0, 0), (0, 1, 0), (0, 0, 1)])
def __init__(
self,
solar_system,
mass=10,
position=(0, 0, 0),
velocity=(0, 0, 0),
):
super(Planet, self).__init__(solar_system, mass, position, velocity)
self.colour = next(Planet.colours)
here is the code from simple_solar_system.py:
# simple_solar_system.py
from solar_system_3d import SolarSystem, Sun, Planet
solar_system = SolarSystem(400, projection_2d=True)
sun = Sun(solar_system)
planets = (
Planet(
solar_system,
position=(150, 50, 0),
velocity=(0, 5, 5),
),
Planet(
solar_system,
mass=20,
position=(100, -50, 150),
velocity=(5, 0, 0)
)
)
while True:
solar_system.calculate_all_body_interactions()
solar_system.update_all()
solar_system.draw_all()
Plz help me, thx a lot!
Python objects have what's called dunder methods, or double underscore. aka magic methods.
Heres what some look like
__lt__(), __le__(), __gt__(), or __ge__()
class Vector:
def __add__(self, vec):
return Vector(self.x + vec.x)
I could say something like and then my Vector classes could be added with the + operator
Heres a better example docs.python - datamodel
I assume you are using this vectors library.
Say you have two vectors:
from vectors import Vector
a = Vector(1, 1, 1)
b = Vector(2, 2, 2)
The Vector class from the vectors library has a function substract(other). You can then use this function to substract vectors in the following way:
a_minus_b = b.substract(a) # a - b
b_minus_a = a.substract(b) # b - a
You could replace all vector minuses in your code with the above code. However, you can also define the __sub__() function on the Vector class like this:
class Vector(Vector):
def __sub__(self, other):
return other.substract(self)
That way a - b will implicitly call the __sub()__ function.
Now you can substract vectors simply as:
a_minus_b = a - b
b_minus_a = b - a
So I created this parabola class which can be instantiated with 3 parameters (a, b and c) or with 3 points belonging to the parabola. The punti() function returns all the points belonging to the parabola in a range defined by n and m. Here's the code (Most of this is in Italian, sorry):
class Parabola:
def __init__(self, tipo=0, *params):
'''
Il tipo รจ 0 per costruire la parabola con a, b, c; 1 per costruire la parabola con
tre punti per la quale passa
'''
if tipo == 0:
self.__a = params[0]
self.__b = params[1]
self.__c = params[2]
self.__delta = self.__b ** 2 - (4 * self.__a * self.__c)
elif tipo == 1:
matrix_a = np.array([
[params[0][0]**2, params[0][0], 1],
[params[1][0]**2, params[1][0], 1],
[params[2][0]**2, params[2][0], 1]
])
matrix_b = np.array([params[0][1], params[1][1], params[2][1]])
matrix_c = np.linalg.solve(matrix_a, matrix_b)
self.__a = round(matrix_c[0], 2)
self.__b = round(matrix_c[1], 2)
self.__c = round(matrix_c[2], 2)
self.__delta = self.__b ** 2 - (4 * self.__a * self.__c)
def trovaY(self, x):
y = self.__a * x ** 2 + self.__b * x + self.__c
return y
def punti(self, n, m, step=1):
output = []
for x in range(int(min(n, m)), int(max(n, m)) + 1, step):
output.append((x, self.trovaY(x)))
return output
Now my little game is about shooting targets with a bow and i have to use the parabola for the trajectory and it passes by 3 points:
The player center
A point with the cursor's x and player's y
A point in the middle with the cursors's y
The trajectory is represented by a black line but it clearly doesn't work and I can't understand why. Here's the code of the game (Don't mind about the bow's rotation, I still have to make it function properly):
import os
import sys
import pygame
from random import randint
sys.path.insert(
1, __file__.replace("pygame-prototype\\" + os.path.basename(__file__), "coniche\\")
)
import parabola
# Initialization
pygame.init()
WIDTH, HEIGHT = 1024, 576
screen = pygame.display.set_mode((WIDTH, HEIGHT))
# Function to rotate without losing quality
def rot_from_zero(surface, angle):
rotated_surface = pygame.transform.rotozoom(surface, angle, 1)
rotated_rect = rotated_surface.get_rect()
return rotated_surface, rotated_rect
# Function to map a range of values to another
def map_range(value, leftMin, leftMax, rightMin, rightMax):
# Figure out how 'wide' each range is
leftSpan = leftMax - leftMin
rightSpan = rightMax - rightMin
# Convert the left range into a 0-1 range (float)
valueScaled = float(value - leftMin) / float(leftSpan)
# Convert the 0-1 range into a value in the right range.
return rightMin + (valueScaled * rightSpan)
# Player class
class Player:
def __init__(self, x, y, width=64, height=64):
self.rect = pygame.Rect(x, y, width, height)
self.dirx = 0
self.diry = 0
def draw(self):
rectangle = pygame.draw.rect(screen, (255, 0, 0), self.rect)
# Target class
class Target:
def __init__(self, x, y, acceleration=0.25):
self.x, self.y = x, y
self.image = pygame.image.load(
__file__.replace(os.path.basename(__file__), "target.png")
)
self.speed = 0
self.acceleration = acceleration
def draw(self):
screen.blit(self.image, (self.x, self.y))
def update(self):
self.speed -= self.acceleration
self.x += int(self.speed)
if self.speed < -1:
self.speed = 0
player = Player(64, HEIGHT - 128)
# Targets init
targets = []
targets_spawn_time = 3000
previous_ticks = pygame.time.get_ticks()
# Ground animation init
ground_frames = []
for i in os.listdir(__file__.replace(os.path.basename(__file__), "ground_frames")):
ground_frames.append(
pygame.image.load(
__file__.replace(os.path.basename(__file__), "ground_frames\\" + i)
)
) # Load all ground frames
ground_frame_counter = 0 # Keep track of the current ground frame
frame_counter = 0
# Bow
bow = pygame.image.load(__file__.replace(os.path.basename(__file__), "bow.png"))
angle = 0
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# Spawning the targets
current_ticks = pygame.time.get_ticks()
if current_ticks - previous_ticks >= targets_spawn_time:
targets.append(Target(WIDTH, randint(0, HEIGHT - 110)))
previous_ticks = current_ticks
screen.fill((101, 203, 214))
player.draw()
for i, e in list(enumerate(targets))[::-1]:
e.draw()
e.update()
if e.x <= -e.image.get_rect().width:
del targets[i]
# Calculating the angle of the bow
mouse_pos = pygame.Vector2(pygame.mouse.get_pos())
angle = map_range(mouse_pos.x, 0, WIDTH, 90, 0)
# Rotate the bow
rotated_bow, rotated_bow_rect = rot_from_zero(bow, angle)
rotated_bow_rect.center = player.rect.center
screen.blit(rotated_bow, rotated_bow_rect)
# Animate the ground
if frame_counter % 24 == 0:
ground_frame_counter += 1
if ground_frame_counter >= len(ground_frames):
ground_frame_counter = 0
for i in range(round(WIDTH / ground_frames[ground_frame_counter].get_rect().width)):
screen.blit(
ground_frames[ground_frame_counter],
(
ground_frames[ground_frame_counter].get_rect().width * i,
HEIGHT - ground_frames[ground_frame_counter].get_rect().height,
),
)
# Calculating the trajectory
mouse_pos.x = (
mouse_pos.x if mouse_pos.x != rotated_bow_rect.centerx else mouse_pos.x + 1
)
# print(mouse_pos, rotated_bow_rect.center)
v_x = rotated_bow_rect.centerx + ((mouse_pos.x - rotated_bow_rect.centerx) / 2)
trajectory_parabola = parabola.Parabola(
1,
rotated_bow_rect.center,
(mouse_pos.x, rotated_bow_rect.centery),
(v_x, mouse_pos.y),
)
trajectory = [(i[0], int(i[1])) for i in trajectory_parabola.punti(0, WIDTH)]
pygame.draw.lines(screen, (0, 0, 0), False, trajectory)
pygame.draw.ellipse(
screen, (128, 128, 128), pygame.Rect(v_x - 15, mouse_pos.y - 15, 30, 30)
)
pygame.draw.ellipse(
screen,
(128, 128, 128),
pygame.Rect(mouse_pos.x - 15, rotated_bow_rect.centery - 15, 30, 30),
)
pygame.display.update()
if frame_counter == 120:
for i in trajectory:
print(i)
frame_counter += 1
You can run all of this and understand what's wrong with it, help?
You round the values of a, b and c to 2 decimal places. This is too inaccurate for this application:
self.__a = round(matrix_c[0], 2)
self.__b = round(matrix_c[1], 2)
self.__c = round(matrix_c[2], 2)
self.__a = matrix_c[0]
self.__b = matrix_c[1]
self.__c = matrix_c[2]
Similar to answer above... rounding is the issue here. This is magnified when the scale of the coordinates gets bigger.
However, disagree with other solution: It does not matter what order you pass the coordinates into your parabola construction. Any order works fine. points are points.
Here is a pic of your original parabola function "drooping" because of rounding error:
p1 = (0, 10) # left
p2 = (100, 10) # right
p3 = (50, 100) # apex
p = Parabola(1, p3, p2, p1)
traj = p.punti(0, 100)
xs, ys = zip(*traj)
plt.scatter(xs, ys)
plt.plot([0, 100], [10, 10], color='r')
plt.show()
Hi there I have the following code and I'm trying to print the object position, I am completely new to python & coding so need a bit of help! This is the code I have;
class object:
def __init__(self, x, y, z, vx, vy, vz):
self.x = x
self.y = y
self.z = z
self.vx = vx
self.vy = vy
self.vz = vz
def position(self):
return '{} {} {}'.format(self.x, self.y, self.z)
obj_1 = object(random.random(), random.random(), random.random(), 0, 0, 0)
print(obj_1.position())
I get the following error message:
AttributeError: 'object' object has no attribute 'position'
Is the indentation causing you a problem? I ran your code after fixing the indentation and it worked fine. Your __init__ function just needed indenting.
import random
class object:
def __init__(self, x, y, z, vx, vy, vz):
self.x = x
self.y = y
self.z = z
self.vx = vx
self.vy = vy
self.vz = vz
def position(self):
return '{} {} {}'.format(self.x, self.y, self.z)
obj_1 = object(random.random(), random.random(), random.random(), 0, 0, 0)
print(obj_1.position())
With fixed indentation:
class object:
def __init__(self, x, y, z, vx, vy, vz):
self.x = x
self.y = y
self.z = z
self.vx = vx
self.vy = vy
self.vz = vz
def position(self):
return '{} {} {}'.format(self.x, self.y,self.z)
obj_1 = object(random.random(), random.random(),random.random(), 0, 0, 0)
print(obj_1.position())
I would like to know why are my variables not changing in the def moving(self) method? Am I missing something? In this object the snake should move (up, down, left, right) depending on the dir_x, dir_y.
The code:
from tkinter import *
from tkinter import ttk
class Snake(object):
def __init__ (self, x, y, dir_x, dir_y, con_x, con_y):
self.x = x
self.y = y
self.dir_x = dir_x
self.dir_y = dir_y
self.con_x = con_x
self.con_y = con_y
self.snakey = root.create_rectangle((self.x+243, self.y+243, self.x+251, self.y+251), fill = "#000000")
def moving(self):
if self.dir_x == 10:
self.x = self.x + 10
root.coords(self.snakey, self.x, self.y, self.dir_x, self.dir_y, self.con_x, self.con_y)
if self.dir_x == -10:
self.x = self.x - 10
root.coords(self.snakey, self.x, self.y, self.dir_x, self.dir_y, self.con_x, self.con_y)
if self.dir_y == 10:
self.y = self.y + 10
root.coords(self.snakey, self.x, self.y, self.dir_x, self.dir_y, self.con_x, self.con_y)
if self.dir_y == -10:
self.y = self.y - 10
root.coords(self.snakey, self.x, self.y, self.dir_x, self.dir_y, self.con_x, self.con_y)
def __str__ (self):
return "<Snake x:%s y:%s dir_x:%s dir_y:%s con_x:%s con_y:%s>" % (self.x, self.y, self.dir_x, self.dir_y, self.con_x, self.con_y)
def moveup(event):
global dy, dx
dy = 10
dx = 0
def movedown(event):
global dy, dx
dy = -10
dx = 0
def moveleft(event):
global dx, dy
dx = -10
dy = 0
def moveright(event):
global dx, dy
dx = 10
dy = 0
win = Tk()
win.title("Snake")
root = Canvas(win, width = 493, height = 493, background = "white")
root.grid(row = 0, column = 0)
x = -1
d, c = 0, 0
xs, xy, dx, dy, cx, cy = 0, 0, 0, 0, 0, 0
for i in range(2, 492, 10):
root.create_line((i, 1, i, 500), fill = "#BFBFBF")
root.create_line((1, i, 500, i), fill = "#BFBFBF")
root.create_rectangle((2, 2, 493, 493), width = 4)
#S1 = Snake(xs, xy, dx, dy, cx, cy)
def Repeat():
S1 = Snake(xs, xy, dx, dy, cx, cy)
print("Tik", dx, dy)
print (S1)
root.after(500, Repeat)
Repeat()
root.bind("w", moveup)
root.bind("s", movedown)
root.bind("a", moveleft)
root.bind("d", moveright)
root.focus_set()
win.mainloop()
You create the method moving, but never call it - it's never executed.
You probably want to call in in repeat. Like this:
def Repeat():
S1 = Snake(xs, xy, dx, dy, cx, cy)
S1.moving ()
print("Tik", dx, dy)
print (S1)
root.after(500, Repeat)
But be warned that the variables will only change inside S1, and not outside - if you're creating it every call of repeat, you'll discard data like S1.x (and so the snake won't move)
How can I see if an x and y are on the edge or inside of the rectangle? Does anyone have any ideas of how to go about that?
I have tried to create a method that will do this called contains point but I am sure I have done this incorrectly
my code:
class Rectangle: # creates class Rectangle
def __init__(self, rX, rY, rW, rH): # initialized so we can call our variables
self.x = rX # x coord
self.y = rY # y coord
self.w = rW # width
self.h = rH # heigh
def __str__(self): # will return string
return 'Rectangle(' + str(self.x) + ',' + str(self.y) + ',' + str(self.w) + ',' + str(self.h)+')'
def right(self): # will check right edge
return self.x + self.w # returns right edge
def bottom(self):
return self.y + self.h
def size(self): # Method Size will obtain size of rectangle
return self.w, self.h
def position(self): # Method will show coords
return self.x, self.y
def area(self): # method will return area of rectangle
return self.w * self.h
def expand(self, offset): # expand will take in an offset value and apply it to a new rectangle
newX = self.x - offset
newY = self.y - offset
newW = self.w + (offset * 2)
newH = self.h + (offset * 2)
newRectangle = Rectangle(newX, newY, newW, newH)
return newRectangle
def contains_point(self, x, y): # contains point will take in 2 coords
# and check to see if they are in the rectangle
if x <= self.x and y <= self.y:
return True
else:
return False
r = Rectangle(30, 40, 100, 110)
print(r.expand(-5), r)
print(r.contains_point(0, 0))
If you have x1, x2 and y1, y2 defining the outer corners of your rectangle, then you can test a point.x, point.y by checking if point.x is between x1 and x2, and point.y is between y1 and y2.
def contains_point(self, x, y): # contains point will take in 2 coords
# and check to see if they are in the rectangle
if (self.x <= x <= self.right() and
self.y <= y <= self.bottom()):
return True
else:
return False