Can't zoom in with gluLookAt(), only zoom out? - python

I'm trying to implement an orbital camera in PyOpenGL Legacy and am trying to make it zoom in and out (so, go forward and back).
This is the relevant bit of code:
def update(self):
self.pos = self.orbitalPos()
print(f"self.pos length {self.magnitude(self.pos)}")
self.look = self.pos * (-1.0)
gluLookAt(*(self.pos), *(self.look), *(self.up))
def moveForward(self):
print("in moveForward")
direction = glm.normalize(self.look)
speed = direction * 0.001
self.pos = self.pos + speed
self.radius = self.magnitude(self.pos)
print(self.radius)
def moveBack(self):
print("in moveBack")
direction = glm.normalize(self.look)
speed = direction * (-0.001)
self.pos = self.pos + speed
self.radius = self.magnitude(self.pos)
print(self.radius)
def orbitalPos(self):
return glm.vec3(
self.radius * math.sin(math.radians(self.phi)) * math.sin(math.radians(self.theta)),
self.radius * math.cos(math.radians(self.phi)),
self.radius * math.sin(math.radians(self.phi)) * math.cos(math.radians(self.theta))
)
def magnitude(self, vec):
return math.sqrt(vec.x ** 2 + vec.y ** 2 + vec.z ** 2)
Up is (0,1,0). In the main function, I call ether moveForward() or moveBack() and then call update().
This is just zooming in and out so phi and theta stay the same, and the only thing that changes is the radius, which is decreasing with moveForward() and increasing with moveBack(), as it should. But in the pygame window the object just keeps getting further away no matter what key I press.
I'm fairly sure I'm borking something in the algebra but I don't know what. Any ideas?

gluLookAt not only sets a matrix, but defines a matrix and multiplies the current matrix (which can be the matrix of the last frame) with the new look at matrix. Therefore you have to load the identity matrix with glLoadIdentity before calling gluLookAt:
def update(self):
self.pos = self.orbitalPos()
print(f"self.pos length {self.magnitude(self.pos)}")
self.look = self.pos * (-1.0)
glLoadIdentity()
gluLookAt(*(self.pos), *(self.look), *(self.up))

Related

particle animation background for kivy

I want to put a particle animation in the background screen of my software, something like the link below, but for Python and kivymd
codepen.io/JulianLaval/pen/KpLXOO
I know this may be difficult or impossible for kivymd right now but if anyone has an idea please let me know
Yes! This is absolutely possible (everything is possible in Kivy). Check out the code below:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line, Color
from random import randint
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import ListProperty
from math import sin, cos
class ParticleMesh(Widget):
points = ListProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.direction = []
self.point_number = 50
Clock.schedule_once(lambda dt: self.plot_points(), 2)
def plot_points(self):
for _ in range(self.point_number):
x = randint(0, self.width)
y = randint(0, self.height)
self.points.extend([x, y])
self.direction.append(randint(0, 359))
Clock.schedule_interval(self.update_positions, 0)
def draw_lines(self):
self.canvas.after.clear()
with self.canvas.after:
for i in range(0, len(self.points), 2):
for j in range(i + 2, len(self.points), 2):
d = self.distance_between_points(self.points[i], self.points[i + 1], self.points[j],
self.points[j + 1])
if d > 120:
continue
color = d / 120
Color(rgba=[color, color, color, 1])
Line(points=[self.points[i], self.points[i + 1], self.points[j], self.points[j + 1]])
def update_positions(self, *args):
step = 1
for i, j in zip(range(0, len(self.points), 2), range(len(self.direction))):
theta = self.direction[j]
self.points[i] += step * cos(theta)
self.points[i + 1] += step * sin(theta)
if self.off_screen(self.points[i], self.points[i + 1]):
self.direction[j] = 90 + self.direction[j]
self.draw_lines()
#staticmethod
def distance_between_points(x1, y1, x2, y2):
return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5
def off_screen(self, x, y):
return x < -5 or x > self.width + 5 or y < -5 or y > self.height + 5
kv = """
FloatLayout:
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
size: self.size
pos: self.pos
ParticleMesh:
canvas:
Color:
rgba: 0, 0, 0, 1
Point:
points: self.points
pointsize: 2
"""
class MeshApp(App):
def build(self):
return Builder.load_string(kv)
if __name__ == '__main__':
MeshApp().run()
This code will create the following (this is just a screenshot - if you run the app, the points move about):
First plot_points is called, which creates an array of points randomly placed on the screen. A random direction for each point is also created. This direction is represented by an angle between 0 and 359. On the completion of this function, a Clock object is instantiated and calls update_positions every frame.
The update_positions moves the particles by one pixel in the angle specified by directions. If the position of the particle is greater than the screen the direction is reversed.
Finally draw_lines is called. This function first clears all existing lines then draws new ones. If the points are at a distance greater than 120 pixels no line is created. However, if they are closer than 120 pixels a line is drawn such that the closer the two points are, the darker the line will be.
You can always increase or decrease the number of points on the screen by changing the self.point_number property.
I will leave it up to you to change the colour of the points and the background - I don't think that should be too hard.

Python Turtle/Tkinter Timers Accelerating

I thought the canonical way to do animation with Python Turtle Graphics was to do something like
def animate():
# move stuff
ontimer(animate, delay)
Looking into the source code for turtle this implements tkinter after() in the background.
Can someone explain why in the program below the animation accelerates and decelerates dramatically when it is left running?
My theory is that since a new .after() id is created each time ontimer() is called, there are somehow multiple timers in existence which interfere with each other? Or maybe it's just a result of the randomness in the program? Or maybe the short interval between callbacks causes problems?
from random import *
from turtle import *
import math
class Vector(object):
def __init__(self, x = 0.0, y = 0.0):
self.x = x
self.y = y
def move(self, other):
""" Move vector by other (in-place)."""
self.__iadd__(other)
def __iadd__(self, other):
if isinstance(other, Vector):
self.x += other.x
self.y += other.y
else:
self.x += other
self.y += other
def rotate(self, angle):
"""Rotate vector counter-clockwise by angle (in-place)."""
radians = angle * math.pi / 180.0
cosine = math.cos(radians)
sine = math.sin(radians)
x = self.x
y = self.y
self.x = x * cosine - y * sine
self.y = y * cosine + x * sine
ant = Vector(0, 0)
aim = Vector(2, 0)
def wrap(value):
"Wrap value around -200 and 200."
if value > 200:
value = -200
elif value < -200:
value = 200
return value
def draw():
"Move ant and draw screen."
ant.move(aim)
ant.x = wrap(ant.x)
ant.y = wrap(ant.y)
aim.move(random() - 0.5)
aim.rotate(random() * 10 - 5)
clear()
goto(ant.x, ant.y)
dot(10)
if running:
ontimer(draw, 50)
setup(420, 420, 370, 0)
hideturtle()
tracer(False)
up()
running = True
draw()
done()
My belief is that your animation accelerates and decelerates because you use tracer() but fail to do an explicit update(). The tracer() function turns off animation but some turtle operations do an implicit update() as a side effect. Since you didn't do an explicit update() you're only getting random updates caused by those side effects.
Below I've added an explicit update() and simplified the code to make the turtle itself the moving object, rather than stamping and clearing. (BTW, if you save the result of stamp() you can ask it to clear itself.)
I've also switched from a circle to a turtle cursor image and added in logic to set the heading to the direction of motion:
from random import random
from turtle import Screen, Turtle, Vec2D
from math import pi, cos, sin
class Vector(object):
def __init__(self, x=0.0, y=0.0):
self.x = x
self.y = y
def move(self, other):
""" Move vector by other (in-place)."""
self.__iadd__(other)
self.wrap()
def __iadd__(self, other):
if isinstance(other, Vector):
self.x += other.x
self.y += other.y
else:
self.x += other
self.y += other
def rotate(self, degrees):
""" Rotate vector counter-clockwise by angle (in-place). """
radians = degrees * pi / 180.0
cosine = cos(radians)
sine = sin(radians)
x = self.x
y = self.y
self.x = x * cosine - y * sine
self.y = y * cosine + x * sine
def position(self):
return Vec2D(self.x, self.y)
def wrap(self):
""" Wrap value around -200 and 200. """
x = self.x
y = self.y
if x > 200:
self.x = -200
elif x < -200:
self.x = 200
if y > 200:
self.y = -200
elif y < -200:
self.y = 200
def draw():
""" Move ant and draw screen. """
ant.move(aim)
position = ant.position()
turtle.setheading(turtle.towards(position))
turtle.setposition(position)
screen.update()
aim.move(random() - 0.5)
aim.rotate(random() * 10 - 5)
screen.ontimer(draw, 75)
screen = Screen()
screen.setup(420, 420)
screen.tracer(False)
turtle = Turtle()
turtle.hideturtle()
turtle.shape('turtle')
turtle.shapesize(0.5)
turtle.penup()
turtle.showturtle()
ant = Vector(0, 0)
aim = Vector(2, 0)
draw()
screen.mainloop()

Pygame - Rotate and move a spaceship (Polygon)

Ok, I've been working all day on this and I haven't found the logic.
I wanna make a classic style asteroids game, and I'm starting with the spaceship.
What I did was draw some lines with the shape of a spaceship:
import pygame
import colors
from helpers import *
class Ship :
def __init__(self, display, x, y) :
self.x = x
self.y = y
self.width = 24
self.height = 32
self.color = colors.green
self.rotation = 0
self.points = [
#A TOP POINT
(self.x, self.y - (self.height / 2)),
#B BOTTOM LEFT POINT
(self.x - (self.width / 2), self.y + (self.height /2)),
#C CENTER POINT
(self.x, self.y + (self.height / 4)),
#D BOTTOM RIGHT POINT
(self.x + (self.width / 2), self.y + (self.height / 2)),
#A TOP AGAIN
(self.x, self.y - (self.height / 2)),
#C A NICE LINE IN THE MIDDLE
(self.x, self.y + (self.height / 4)),
]
def move(self, strdir) :
dir = 0
if strdir == 'left' :
dir = -3
elif strdir == 'right' :
dir = 3
self.points = rotate_polygon((self.x, self.y), self.points, dir)
def draw(self, display) :
stroke = 2
pygame.draw.lines(display, self.color, False, self.points, stroke)
The ship looks like this:
Now important things to know:
The tuple (self.x, self.y) is the middle of the spaceship.
Using this function I managed to rotate (spin) it on command using the keys A and D
def rotate_polygon(origin, points, angle) :
angle = math.radians(angle)
rotated_polygon = []
for point in points :
temp_point = point[0] - origin[0] , point[1] - origin[1]
temp_point = (temp_point[0] * math.cos(angle) - temp_point[1] * math.sin(angle),
temp_point[0] * math.sin(angle) + temp_point[1] * math.cos(angle))
temp_point = temp_point[0] + origin[0], temp_point[1] + origin[1]
rotated_polygon.append(temp_point)
return rotated_polygon
The problem is: How can I make it move forward or backwards in the direction the spaceship is pointing?
OR
How can I update the self.x and self.y values and update them inside the self.points list and preserve rotation?
The easiest, most general way to deal with movement and rotation would to use some vector math (can apply to 3D graphics as well). You could keep a 2D vector representing the forward direction of your ship. For example, if your ship starts facing upwards and your (0,0) coordinate is the top left. You could do.
self.forward = Vector2D(0, -1) # Vector2D(x, y)
When you rotate you must rotate this vector. You can rotate using the following.
self.forward.x = self.forward.x * cos(angle) - self.forward.y * sin(angle)
self.forward.y = self.forward.x * sin(angle) + self.forward.y * cos(angle)
Then when you want to move the ship you can transform the ship points relative to this vector. For example.
self.x += forward.x * velocity.x
self.y += forward.y * velocity.y
I would highly recommend you write a little Vector2D class which can do some of the basic operations, e.g. dot, cross, mult, add, sub, normalize, etc.
If you are familiar with matrices then these operations can become easier if you implement them using matrices instead of a system of linear equations.
It seems to me that you should be able to simply do the following.
def updatePosition(self, dx, dy):
self.x += dx
self.y += dy
newPoints = []
for (x,y) in self.points:
newPoints.append((x+dx, y+dy))
self.points = newPoints

"tkinter.TclError: invalid command name" error after calling root.destroy()

I am in the process of learning tkinter on Python 3.X. I am writing a simple program which will get one or more balls (tkinter ovals) bouncing round a rectangular court (tkinter root window with a canvas and rectangle drawn on it).
I want to be able to terminate the program cleanly by pressing the q key, and have managed to bind the key to the root and fire the callback function when a key is pressed, which then calls root.destroy().
However, I'm still getting errors of the form _tkinter.TclError: invalid command name ".140625086752360" when I do so. This is driving me crazy. What am I doing wrong?
from tkinter import *
import time
import numpy
class Ball:
def bates():
"""
Generator for the sequential index number used in order to
identify the various balls.
"""
k = 0
while True:
yield k
k += 1
index = bates()
def __init__(self, parent, x, y, v=0.0, angle=0.0, accel=0.0, radius=10, border=2):
self.parent = parent # The parent Canvas widget
self.index = next(Ball.index) # Fortunately, I have all my feathers individually numbered, for just such an eventuality
self.x = x # X-coordinate (-1.0 .. 1.0)
self.y = y # Y-coordinate (-1.0 .. 1.0)
self.radius = radius # Radius (0.0 .. 1.0)
self.v = v # Velocity
self.theta = angle # Angle
self.accel = accel # Acceleration per tick
self.border = border # Border thickness (integer)
self.widget = self.parent.canvas.create_oval(
self.px() - self.pr(), self.py() - self.pr(),
self.px() + self.pr(), self.py() + self.pr(),
fill = "red", width=self.border, outline="black")
def __repr__(self):
return "[{}] x={:.4f} y={:.4f} v={:.4f} a={:.4f} r={:.4f} t={}, px={} py={} pr={}".format(
self.index, self.x, self.y, self.v, self.theta,
self.radius, self.border, self.px(), self.py(), self.pr())
def pr(self):
"""
Converts a radius from the range 0.0 .. 1.0 to window coordinates
based on the width and height of the window
"""
assert self.radius > 0.0 and self.radius <= 1.0
return int(min(self.parent.height, self.parent.width)*self.radius/2.0)
def px(self):
"""
Converts an X-coordinate in the range -1.0 .. +1.0 to a position
within the window based on its width
"""
assert self.x >= -1.0 and self.x <= 1.0
return int((1.0 + self.x) * self.parent.width / 2.0 + self.parent.border)
def py(self):
"""
Converts a Y-coordinate in the range -1.0 .. +1.0 to a position
within the window based on its height
"""
assert self.y >= -1.0 and self.y <= 1.0
return int((1.0 - self.y) * self.parent.height / 2.0 + self.parent.border)
def Move(self, x, y):
"""
Moves ball to absolute position (x, y) where x and y are both -1.0 .. 1.0
"""
oldx = self.px()
oldy = self.py()
self.x = x
self.y = y
deltax = self.px() - oldx
deltay = self.py() - oldy
if oldx != 0 or oldy != 0:
self.parent.canvas.move(self.widget, deltax, deltay)
def HandleWallCollision(self):
"""
Detects if a ball collides with the wall of the rectangular
Court.
"""
pass
class Court:
"""
A 2D rectangular enclosure containing a centred, rectagular
grid of balls (instances of the Ball class).
"""
def __init__(self,
width=1000, # Width of the canvas in pixels
height=750, # Height of the canvas in pixels
border=5, # Width of the border around the canvas in pixels
rows=1, # Number of rows of balls
cols=1, # Number of columns of balls
radius=0.05, # Ball radius
ballborder=1, # Width of the border around the balls in pixels
cycles=1000, # Number of animation cycles
tick=0.01): # Animation tick length (sec)
self.root = Tk()
self.height = height
self.width = width
self.border = border
self.cycles = cycles
self.tick = tick
self.canvas = Canvas(self.root, width=width+2*border, height=height+2*border)
self.rectangle = self.canvas.create_rectangle(border, border, width+border, height+border, outline="black", fill="white", width=border)
self.root.bind('<Key>', self.key)
self.CreateGrid(rows, cols, radius, ballborder)
self.canvas.pack()
self.afterid = self.root.after(0, self.Animate)
self.root.mainloop()
def __repr__(self):
s = "width={} height={} border={} balls={}\n".format(self.width,
self.height,
self.border,
len(self.balls))
for b in self.balls:
s += "> {}\n".format(b)
return s
def key(self, event):
print("Got key '{}'".format(event.char))
if event.char == 'q':
print("Bye!")
self.root.after_cancel(self.afterid)
self.root.destroy()
def CreateGrid(self, rows, cols, radius, border):
"""
Creates a rectangular rows x cols grid of balls of
the specified radius and border thickness
"""
self.balls = []
for r in range(1, rows+1):
y = 1.0-2.0*r/(rows+1)
for c in range(1, cols+1):
x = 2.0*c/(cols+1) - 1.0
self.balls.append(Ball(self, x, y, 0.001,
numpy.pi/6.0, 0.0, radius, border))
def Animate(self):
"""
Animates the movement of the various balls
"""
for c in range(self.cycles):
for b in self.balls:
b.v += b.accel
b.Move(b.x + b.v * numpy.cos(b.theta),
b.y + b.v * numpy.sin(b.theta))
self.canvas.update()
time.sleep(self.tick)
self.root.destroy()
I've included the full listing for completeness, but I'm fairly sure that the problem lies in the Court class. I presume it's some sort of callback or similar firing but I seem to be beating my head against a wall trying to fix it.
You have effectively got two mainloops. In your Court.__init__ method you use after to start the Animate method and then start the Tk mainloop which will process events until you destroy the main Tk window.
However the Animate method basically replicates this mainloop by calling update to process events then time.sleep to waste some time and repeating this. When you handle the keypress and terminate your window, the Animate method is still running and attempts to update the canvas which no longer exists.
The correct way to handle this is to rewrite the Animate method to perform a single round of moving the balls and then schedule another call of Animate using after and provide the necessary delay as the after parameter. This way the event system will call your animation function at the correct intervals while still processing all other window system events promptly.

Python: Making a projectile bounce on the GraphWin window along all walls?

Extend the projectile and tracker to implement the following graphics game:
a. There is square box of a given dimension (choose one reasonable size box) in which
there is a projectile which randomly bounces of the four walls.
b. When a projectile hits a wall, it is launched again by randomly selecting an angle in
the allowed range and a random velocity in a given range. For example, if the
projectile hits the bottom boundary, it selects a random angle in the range 0 to 180
degrees; if it hits the right vertical wall then, it selects random angle between 90 and
270 degrees; and so on.
c. Instead of drawing the circle, move the circle and also fill it with some color.
d. Extend to two projectiles.
Don't ask user the parameters. Use a set of parameters that you think the best.
So I am having trouble trying to find how to make two cballs (the projectiles) bounce from wall to wall. I figured how to bounce them off the bottom wall of the window but it doesn't stop at the right wall and bounce from there. Not only that I have no idea on how to make the balls follow instead of continuously printing circles to Track the movement of the balls. I use ".undraw()" but its very jumpy and not smooth at all.
I would appreciate the help very much! The assignment is due tomorrow and this is what i have:
# projectilebounce.py
# Projectile hits a wall and again is launced
from math import sin, cos, radians
from graphics import *
class Projectile:
def __init__(self, angle, velocity, height):
self.xpos = 0.0
self.ypos = height
theta = radians(angle)
self.xvel = velocity * cos(theta)
self.yvel = velocity * sin(theta)
def update(self, time):
self.xpos = self.xpos + time * self.xvel
yvel1 = self.yvel - 9.8 * time
self.ypos = self.ypos + time * (self.yvel + yvel1) / 2.0
self.yvel = yvel1
def getY(self):
return self.ypos
def getX(self):
return self.xpos
def reset(self, angle, velocity, height):
self.xpos = self.getX()
self.ypos = height
theta = radians(angle)
self.xvel = velocity * cos(theta)
self.yvel = velocity * sin(theta)
class Tracker:
def __init__(self, window, objToTrack):
self.win = window
self.obj = objToTrack
def update(self):
a = Circle( Point(self.obj.getX(), self.obj.getY()), 5)
a.draw(self.win)
a.setFill("Black")
a.undraw()
def getInputs():
a1 = 60
a2 = 30
v1 = 60
v2 = 60
h1 = 10
h2 = 10
t = 0.1
return a1, a2, v1, v2, h1, h2, t
def main():
print("This program graphically depicts the flight of a cannonball.\n")
win = GraphWin("TRACKER", 600, 500)
win.setCoords(0.0, 0.0, 600, 500)
angle1, angle2, vel1, vel2, h01, h02, time = getInputs()
cball1 = Projectile(angle1, vel1, h01)
cball2 = Projectile(angle2, vel2, h02)
tracker_cball1 = Tracker(win, cball1)
tracker_cball2 = Tracker(win, cball2)
while True:
if cball1.getY() <= 0: #<---ball 1
cball1.reset(60, 60, 10)
else:
cball1.update(time)
tracker_cball1.update()
if cball2.getY() <= 0: #<---ball 2
cball2.reset(30, 60, 10)
else:
cball2.update(time)
tracker_cball2.update()
if __name__ == '__main__' : main()
Thank you!, I am a big noob at coding at the moment. Much appreciated!

Categories

Resources