How do I check if two balls collide in tkinter? - python

Working on a small project involving tkinter and I need to figure out how to get balls bouncing off of each other.
Heres the code:
from tkinter import *
# dimensions of canvas
WIDTH=300
HEIGHT=400
# Create window and canvas
window = Tk()
canvas = Canvas(window, width=WIDTH, height=HEIGHT, bg='#ADF6BE')
canvas.pack()
# starting position of ball
x = 0
y = 10
# starting position of ball1
x1 = 100
y1 = 0
# distance moved each time step for ball 1
dx = 10
dy= 10
# distance moved each time step for ball 2
dx1 = 10
dy1 = 10
# diameter of ball
ballsize = 30
while True:
x = x + dx
y = y + dy
x1 = x1 + dx1
y1 = y1 + dy1
# if ball get to edge then we need to
# change direction of movement
if x >= WIDTH-ballsize or x <= 0 or x == x1:
dx = -dx
print("x=", x)
print('y=', y)
if y >= HEIGHT-ballsize or y <= 0 or y == y1:
dy = -dy
print("x=", x)
print('y=', y)
if x1 >= WIDTH-ballsize or x1 <= 0 or x1 == x:
dx1 = -dx1
print("x1=", x1)
print('y1=', y1)
if y1 >= HEIGHT-ballsize or y1 <= 0 or y1 == y:
dy1 = -dy1
print("x1=", x1)
print('y1=', y1)
# Create balls
ball=canvas.create_oval(x, y, x+ballsize, y+ballsize, fill="white", outline='white')
ball1 = canvas.create_oval(x1, y1, x1 + ballsize, y1 + ballsize, fill="white", outline='white')
# display ball
canvas.update()
canvas.after(50)
#remove ball
canvas.delete(ball)
canvas.delete(ball1)
window.mainloop()
They move and bounce off of the canvas walls but not off of each other.
Heres an image to show what I mean, instead of hitting each other and bouncing off

You have to check the distance between the balls.
If the distance between the center of the two circles is less than the sum of the radii of the circles, then they are colliding.
(math.sqrt((ball1.x- ball2.x) ** 2 + (ball1.y - ball2.y) ** 2) <= sum_radii
Then change the dy and dx of the balls.

Related

How do I get the minimum distance between a point and a parabola using Python?

What I want to make is angrybirds game.
There is a requirement
1.Draw a rectangle randomly between 100 and 200 in length and length 10 in length.
2. Receive the user inputting the launch speed and the launch angle.
3. Project shells square from origin (0,0).
4. If the shell is hit, we'll end it, or we'll continue from number two.
So this is what I wrote
import turtle as t
import math
import random
def square():
for i in range(4):
t.forward(10)
t.left(90)
def fire():
x = 0
y = 0
speed = int(input("속도:"))
angle = int(input("각도:"))
vx = speed * math.cos(angle * 3.14/180.0)
vy = speed * math.sin(angle * 3.14/180.0)
while t.ycor() >= 0:
vx = vx
vy = vy - 10
x = x + vx
y = y + by
t.goto(x,y)
d = t.distance(d1+5, 5)
if d < 10:
print("Game End")
else:
t.up()
t.goto(0,0)
t.down()
fire()
d1 = random.randint(100,200)
t.up()
t.forward(d1)
t.down()
square()
t.up()
t.goto(0,0)
t.down()
fire()
I want to get an answer to this problem.
The problem is I want to calculate a minimum distance between a point(target point is (d1+5,5)) and a parabola which turtle draw. so I try to find an answer searching in google, python book, but I can't find it.
please help me
You can use basic calculus to determine this minimum distance.
Let's suppose you have point (x1, y1) and parabola y = ax^2 + bx + c
Distance between point and parabola at specific x let's name d(x)
d(x) = sqrt((x-x1)^2 + (ax^2 + bx + c - y1)^2)
We can find local minimum by calculating derivative and equate to 0 (we can ignore squared root and 2 - increasing functions won't change minimum):
x-x1 + (b + 2ax)(c + bx + ax^2 - y1) = 0
This is cubic equation, which you can solve analytically (https://en.wikipedia.org/wiki/Cubic_equation) or numerically (here you can use numpy, https://docs.scipy.org/doc/numpy/reference/generated/numpy.roots.html)
Those 2 methods will give you x coordinate of point on parable minimizing the distance. Now you just have to calculate it, using d(x) formula.
There's a bug in your game: the fire's y coordinate often goes way below 0, making it hard to end up with the box.
import turtle as t
from math import sin,cos,pi
import random
def square():
for i in range(4):
t.forward(10)
t.left(90)
def fire():
x = 0
y = 0
speed = int(input("속도:"))
angle = int(input("각도:"))
vx = speed *cos(angle * pi/180.0)
vy = speed * sin(angle * pi/180.0)
while True:
vx = vx
vy = vy - 10
x = x + vx
if y + vy >= -10:
y = y + vy
t.goto(x,y)
else:
break
d = t.distance(d1+5, 5)
print(d)
if d < 10:
print("Game End")
else:
t.up()
t.home()
t.down()
fire()
d1 = random.randint(100,200)
t.up()
t.forward(d1)
t.down()
square()
t.up()
t.home()
t.down()
fire()

Area of a Rectangular in a Plane

I am new to programming and I have difficulties with solving a problem. I am coding it in python. May I have some help, please? So the condition says: A rectangular is set on 2 of its opposite angles (x1, x2) and (y1, y2). Find the area and the perimeter of the rectangular. The input is read from the console. The numbers x1, x2, y1, y2 are given one by one on a line.
Inputs and Outputs:
An example:
my code:
x1 = float(raw_input("x1 = "))
y1 = float(raw_input("y1 = "))
x2 = float(raw_input("x2 = "))
y2 = float(raw_input("y2 = "))
if x1 > x2 and y1 < y2:
a = x1 - x2
b = y2 - y1
else:
a = x2 - x1
b = y1 - y1
area = a * b
perimeter = 2 * (a + b)
print area
print perimeter
You are on the right track!
Let me make a few suggestions:
Inputs
There is nothing wrong with this. (Unless you want to use Python3, where where raw_input is now just input)
x1 = float(raw_input("x1 = "))
y1 = float(raw_input("y1 = "))
x2 = float(raw_input("x2 = "))
y2 = float(raw_input("y2 = "))
Width and Height of the rectangle
If you use the builtin function abs(), you do not have to worry about the sign of (x1 - x2) and (y1 - y2)! Where abs(x) gives the absolute value of x.
width = abs(x1 - x2)
height = abs(y1 - y2)
Area and perimeter
Now that we have the height and width, we can use you code to calculate the area and perimeter:
area = height * width
perimeter = 2 * (height + width)
Bonus
Check if your rectangle is a square:
if height == width:
print "It's a square!"
Putting everything together:
x1 = float(raw_input("x1 = "))
y1 = float(raw_input("y1 = "))
x2 = float(raw_input("x2 = "))
y2 = float(raw_input("y2 = "))
width = abs(x1 - x2)
height = abs(y1 - y2)
area = height * width
perimeter = 2 * (height + width)
print area
print perimeter
if height == width:
print "It's a square!"
Let me know if you need me to explain anything!

Is there a better way to find points along a curve than Bresenham's line algorithm

I have a travel time map, I want to get the integer points along the shortest path from source to receiver.
My present solution is that I make a runge-kutta integration from the receiver location and get a series of float points. Then I sample every 5 or some number of points and assume it a straight line between in order to use the Bresenham's line algorithm. With this approach, I will get the integer points.
However, it's not enough fast. Because I need to calculate a lot of receivers' shortest path, the sum of time will be very large.
I used line_profiler to analysis the time-consuming, which shows the major part of time is for function ruge-kutta and its calling function get_velocity
codes are below
def optimal_path_2d(gradx_interp,
grady_interp,
starting_point,
dx,
N=100):
"""
Find the optimal path from starting_point to the zero contour
of travel_time. dx is the grid spacing
Solve the equation x_t = - grad t / | grad t |
"""
def get_velocity(position):
""" return normalized velocity at pos """
x, y = position
vel = np.array([gradx_interp(y, x)[0][0], grady_interp(y, x)[0][0]])
return vel / np.linalg.norm(vel)
def runge_kutta(pos, ds):
""" Fourth order Runge Kutta point update """
k1 = ds * get_velocity(pos)
k2 = ds * get_velocity(pos - k1 / 2.0)
k3 = ds * get_velocity(pos - k2 / 2.0)
k4 = ds * get_velocity(pos - k3)
return pos - (k1 + 2 * k2 + 2 * k3 + k4) / 6.0
x = runge_kutta(starting_point, dx)
xl, yl = [], []
for i in range(N):
xl.append(x[0])
yl.append(x[1])
x = runge_kutta(x, dx)
distance = ((x[0] - xl[-1])**2 +
(x[1] - yl[-1])**2)**0.5
if distance < dx*0.9:
break
return yl, xl
def get_curve(x_curve, y_curve, num_interval):
"""Curve Algorithm based on Bresenham's Line Algorithm
Produces a list of tuples
"""
num = len(x_curve)
if num < num_interval:
print("num_interval is too large.")
ret_set = set()
x0 = x_curve[0]
y0 = y_curve[0]
for i in range(num_interval, num, num_interval):
x1 = x_curve[i]
y1 = y_curve[i]
points_on_line = get_line((x0, y0), (x1, y1))
ret_set.update(points_on_line)
x0 = x1
y0 = y1
if num % num_interval != 0:
n = int(num/num_interval)*num_interval
x0 = x_curve[n]
y0 = y_curve[n]
x1 = x_curve[-1]
y1 = y_curve[-1]
points_on_line = get_line((x0, y0), (x1, y1))
ret_set.update(points_on_line)
return list(ret_set)
def get_line(start, end):
"""modifed version of Bresenham's Line Algorithm
Produces a list of tuples from start and end
>>> points1 = get_line((0, 0), (3, 4))
>>> points2 = get_line((3, 4), (0, 0))
>>> assert(set(points1) == set(points2))
>>> print points1
[(0, 0), (1, 1), (1, 2), (2, 3), (3, 4)]
>>> print points2
[(3, 4), (2, 3), (1, 2), (1, 1), (0, 0)]
"""
# Setup initial conditions
x1, y1 = (int(x) for x in start)
x2, y2 = (int(x) for x in end)
dx = x2 - x1
dy = y2 - y1
# Determine how steep the line is
is_steep = abs(dy) > abs(dx)
# Rotate line
if is_steep:
x1, y1 = y1, x1
x2, y2 = y2, x2
# Swap start and end points if necessary and store swap state
swapped = False
if x1 > x2:
x1, x2 = x2, x1
y1, y2 = y2, y1
swapped = True
# Recalculate differentials
dx = x2 - x1
dy = y2 - y1
# Calculate error
error = int(dx / 2.0)
ystep = 1 if y1 < y2 else -1
# Iterate over bounding box generating points between start and end
y = y1
points = []
for x in range(x1, x2 + 1):
coord = (y, x) if is_steep else (x, y)
points.append(coord)
error -= abs(dy)
if error < 0:
y += ystep
error += dx
# Reverse the list if the coordinates were swapped
if swapped:
points.reverse()
return points
nx = 100
ny = 100
num_interval = 5
loc_src = (10, 10)
loc_rec = (70, 90)
coordx = np.arange(nx)
coordy = np.arange(ny)
X, Y = np.meshgrid(coordx, coords)
travel_time = (X-loc_src[0])**2/5 + (Y-loc_src[1])**2/10 # for simplicity
grad_t_y, grad_t_x = np.gradient(travel_time, dx)
if isinstance(travel_time, np.ma.MaskedArray):
grad_t_y[grad_t_y.mask] = 0.0
grad_t_y = grad_t_y.data
grad_t_x[grad_t_x.mask] = 0.0
grad_t_x = grad_t_x.data
gradx_interp = RectBivariateSpline(coordy, coordx, grad_t_x)
grady_interp = RectBivariateSpline(coordy, coordx, grad_t_y)
yl, xl = optimal_path(gradx_interp, grady_interp, loc_rec, dx)
grid_indx = get_curve(xl, yl, num_interval)
I hear that Cython will be faster, then I learn a little recently and try it. the result is only 2 faster than codes above because I'm really new to Cython. The code below is incomplete, and I just wrote it for testing.
import numpy as np
from numpy.core.umath_tests import inner1d
def func(X_interp, Y_interp):
def get_velocity(double x, double y ):
""" return normalized velocity at pos """
cdef double vel[2], norm
a = X_interp(y, x)
vel[0] = a[0][0]
b = Y_interp(y, x)
vel[1] = b[0][0]
# norm = (vel[0]**2 + vel[1]**2)**0.5
# vel[0] = vel[0]/norm
# vel[1] = vel[1]/norm
return vel
def runge_kutta(double x, double y, double ds):
""" Fourth order Runge Kutta point update """
cdef double k1[2], k2[2], k3[2], k4[2], r[2], pos[2]
pos[0] = x; pos[1] = y
k1 = get_velocity(pos[0], pos[1])
k2 = get_velocity(pos[0] - k1[0]/2.0*ds,pos[1] - k1[1]/2.0*ds)
k3 = get_velocity(pos[0] - k2[0]/2.0*ds,pos[1] - k2[1]/2.0*ds)
k4 = get_velocity(pos[0] - k3[0]/2.0*ds,pos[1] - k3[1]/2.0*ds)
cdef size_t i
for i in range(2):
r[i] = pos[i] - ds * (k1[i] + 2*k2[i] + 2*k3[i] + k4[i])/6.0
return r
for i in range(50):
runge_kutta(0, 0, 1.)
# print(runge_kutta(0, 0, 1.))

python find vector direction between two 3D points

I'm trying to calculate the direction of a 3D vector starting at point (x, y, z) and ending at point (a, b, c) for the navigation in my spaceship game, but I have been unable to find anything helpful. So far I have tried using two circles, one for figure out x and y and another for z, to figure it out and the code only works if the two vector's distances are very similar.
Here is what I'm using:
def setcourse(self, destination):
x1, y1, z1 = self.coords
x2, y2, z2 = destination
dx = x2 - x1
dy = y2 - y1
dz = z2 - z1
self.heading = math.atan2(dy, dx)
self.heading2 = math.atan2(dz, dy)
self.distance = int(math.sqrt((dx) ** 2 + (dy) ** 2))
self.distance2 = int(math.sqrt((dy) ** 2 + (dz) ** 2))
def move(self):
if self.distance > 0 and self.distance2 > 0:
if self.atwarp == True:
x, y, z = self.coords
x += math.cos(self.heading) * self.speed
y += math.sin(self.heading) * self.speed
z += math.sin(self.heading2) * self.speed
self.coords = (x, y, z)
print(str(self.coords))
self.distance -= self.speed
self.distance2 -= self.speed
elif self.distance <= 0 and self.distance2 <= 0 and self.atwarp == True:
self.atwarp = False
self.speed = 0
print("Reached Destination")
else:
self.atwarp = False
I'm not sure how much of it is a math error and how much is a programming one, but the z winds up way off and I'm not sure how to go about fixing it. No matter what I do the z is always off if its input more than slightly different from the others.
Here is examples starting from (0, 0, 0). I'm trying to get the output to be similar if not the same as the input.
Input: (100, 200, -200)
Vector1 heading in radians: 1.1071487177940904
Vector2 heading: 2.356194490192345
Vector1 distance: 223
Vector2 distance: 282
Output: (99.7286317964909, 199.4572635929818, 157.68481220460077)
The x and y are fine, but the z is off.
Input: (-235, 634, -21)
Vector1 heading in radians: 1.9257588105240444
Vector2 heading: 1.6039072496758664
Vector1 distance: 676
Vector2 distance: 634
Output: (-220.3499891866359, 594.4761410396925, 633.6524941214135)
The z off.
The direction of the movement is the trio dx, dy, dz you calculated. This vector is not pure:
it contains distance and direction. If you want direction alone, you have to normalize
this:
The distance is sqrt(dx^2 + dy^2 + dz^2).
For the normalized direction, you divide each dx, dy, and dz by this number.
If you want to move in that direction, the new position is the old position plus
the the direction vector times the distance you want to travel:
newpos = oldpos + dist * dirvector
I'm not sure what you mean by input: (100, 200, -200) if that is the direction,
your direction vector would be 300 long, and the actual direction vector is
100/300, 200/300, and -200/300 (so 0.333, 0.667 and -0.667)
If you want to travel 500 along that direction, the new position is
0+166.67, 0+333.33, and 0-333.33

Circle and line collision detection in python tkinter

I writing a python program in which a circle bounces off of user drawn lines. There are multiple circles that bounce off the wall. For each one, the shortest distance from the center of the circle and the ball should be calculated. I would prefer if this code was very efficient because my current algorithm lags the computer a lot. If point a is the starting point ,and point b is the end point, and point c is the center, and r is the radius, how would I calculate the shortest distance between the ball? This algorithm should also work if the X coordinate of the ball is out of range of x coordinates in segment AB.
Please post python code
Any help would be appreciated!
Here's what I have so far:
lineList is a list with 4 values that contains beginning and end coordinates of the user drawn lines
center is the center of the ball
global lineList, numobjects
if not(0 in lineList):
beginCoord = [lineList[0],lineList[1]]
endCoord = [lineList[2]-500,lineList[3]-500]
center = [xCoordinate[i],yCoordinate[i]+15]
distance1 = math.sqrt((lineList[1] - center[1])**2 + (lineList[0] - center[0])**2)
slope1 = math.tan((lineList[1] - lineList[3]) / (lineList[0] - lineList[2]))
try:
slope2 = math.tan((center[1] - beginCoord[1])/(center[0]-beginCoord[0]))
angle1 = slope2 + slope1
circleDistance = distance1 * math.sin(angle1)
except:
#If the circle is directly above beginCoord
circleDistance = center[1] - lineList[1]
global numbounces
if circleDistance < 2 and circleDistance > -2:
print(circleDistance)
b = False
b2=False
if xCoordinate[i] < 0:
xCoordinate[i] += 1
speed1[i] *= -1
b=True
elif xCoordinate[i] > 0:
xCoordinate[i] -= 1
speed1[i] *= -1
b=True
if yCoordinate[i] < 0:
yCoordinate[i] += 1
speed2[i] *= -1
b2=True
elif yCoordinate[i] > 0:
yCoordinate[i] -= 1
speed2[i] *= -1
b2=True
if b and b2:
#Only delete the line if the ball reversed directions
numbounces += 1
#Add a ball after 5 bounces
if numbounces % 5 == 0 and numbounces != 0:
numobjects = 1
getData(numobjects)
canvas.delete("line")
lineList = [0,0,0,0]
I don't know what is the mean of shortest distance between the ball, but if you want to calculation the point where the circle will contact the line you can use sympy to figure the formula:
from sympy import *
from sympy.geometry import *
x1, y1, x2, y2, xc, yc = symbols("x1,y1,x2,y2,xc,yc")
p1 = Point(x1, y1)
p2 = Point(x2, y2)
pc = Point(xc, yc)
line = Line(p1, p2)
pline = line.perpendicular_line(pc)
p = line.intersection(pline)[0]
cse(p, symbols=numbered_symbols("t"))
the output is :
([(t0, x1 - x2), (t1, y1 - y2), (t2, x1*y2 - x2*y1), (t3, t0**2 + t1**2)],
[Point((t0**2*xc + t0*t1*yc - t1*t2)/t3, (t0*t1*xc + t0*t2 + t1**2*yc)/t3)])
this means that you can calculate the perpendicular point as:
t0 = x1 - x2
t1 = y1 - y2
t2 = x1*y2 - x2*y1
t3 = t0**2 + t1**2
xp = (t0**2*xc + t0*t1*yc - t1*t2)/t3
yp = (t0*t1*xc + t0*t2 + t1**2*yc)/t3

Categories

Resources