Circle and line collision detection in python tkinter - python

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

Related

How do I check if two balls collide in tkinter?

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.

Given three (x,y) points in (quadrant i) that make a right angle triangle, find triangle oriantation

The problem seems to be straight forward, however my math isn't great, thus proving challenging. At this point I've almost come to the conclusion that there may not be a solution, however the little I do know of math there almost always seems to be a solution for those of you better informed.
I have three (x,y) verities in 2D plane, they form an (almost) right angle triangle (i.e. 94 or 87 degrees this does vary, however always with in a few degrees). Vertices are always in the positive axis "Q1", (I'm working with pixel data).
What I need is to find orientation of the triangle in 360 degrees:
legs (opp & adj) are always against the (imaginary) x,y axis for all four quadrants, so hypotenuse is always facing outwards
Triangle in:
q i - 90 degrees
q ii - 180 degrees
q iii - 270 degrees
q iv - 0 degrees
I have tried a number of solutions from various suggestions on this forum and else where, at the moment I'm working with this solution, however its not entirely working for me.
https://stackoverflow.com/a/15442539/14398314
Any advice would much be appreciated, language used is python.
Edit: The following image represents input data and required solution.
Image of input and output data
Cheers
Step 1: identify the corner of the triangle which is near the (almost) right angle
If the 3 corners are A,B,C then build the dot-product AB*BC, AC*CB, AB*AC. The lowest dot product is the one with sides that are near a right angle, and the corner that appears twice in the dot product is the corner of the near right angle.
Step 2: now that you know the corner that is almost right angle (let's say it was A), compute for sides AB and AC the displacement in x and y (AB_DeltaX, AB_DeltaY, AC_DeltaX, AC_DeltaY) and keep, for each side, only the one with the largest absolute value.
The quadrants can be found by this table:
90 : deltaX and deltaY are both positive
180: deltaX < 0 and deltaY > 0
270: both negative
0 : deltaY <0, deltaX > 0
Here a simple implementation:
a = (4.2,0.1)
b = (3.1,4.2)
c = (0.1,3.2)
def get_q(a,b,c):
#step 1
ab = (b[0] - a[0], b[1] - a[1])
ac = (c[0] - a[0], c[1] - a[1])
ba = (a[0] - b[0], a[1] - b[1])
bc = (c[0] - b[0], c[1] - b[1])
ca = (a[0] - c[0], a[1] - c[1])
cb = (b[0] - c[0], b[1] - c[1])
dp1 = abs(ab[0] * bc[0] + ab[1] * bc[1])
dp2 = abs(ac[0] * cb[0] + ac[1] * cb[1])
dp3 = abs(ab[0] * ac[0] + ab[1] * ac[1])
# find minimum
if dp1 < dp2 and dp1 < dp3:
corner = 'B'
delta1 = ba
delta2 = bc
elif dp2 < dp1 and dp2 < dp3:
corner = 'C'
delta1 = ca
delta2 = cb
elif dp3 < dp1 and dp3 < dp2:
corner = 'A'
delta1 = ab
delta2 = ac
else:
corner = 'unknown'
delta1 = (0.0, 0.0)
delta2 = (0.0, 0.0)
# Step 2
if abs(delta1[0]) > abs(delta2[0]):
deltaX = delta1[0]
else:
deltaX = delta2[0]
if abs(delta1[1]) > abs(delta2[1]):
deltaY = delta1[1]
else:
deltaY = delta2[1]
if deltaX > 0 and deltaY > 0:
quadrant = 'Q1'
elif deltaX < 0 and deltaY < 0:
quadrant = 'Q3'
elif deltaX < 0 and deltaY > 0:
quadrant = 'Q2'
elif deltaX > 0 and deltaY < 0:
quadrant = 'Q4'
else:
quadrant = 'unknown'
return quadrant

Distance from point to two line segments

I'm rather new to coding and am trying to check whether a dot drawn (in pygame)is on the last line drawn (making the sprouts game).
I have two lists, both holding coordinates of the line segments (in 30pix) just drawn, and one with last dot drawn.
current_line = []
dot_pos = []
Distance function I found online:
def dist_point_to_line(line1, line2, point):
x0 = point[0]
y0 = point[1]
x1 = line1[0]
y1 = line1[1]
x2 = line2[0]
y2 = line2[1]
px = x2-x1
py = y2-y1
norm = px*px + py*py
u = ((x0 - x1) * px + (y0 - y1) * py) / float(norm)
if u > 1:
u = 1
elif u < 0:
u = 0
x = x1 + u * px
y = y1 + u * py
dx = x - x0
dy = y - y0
dist = sqrt(dx*dx + dy*dy)
return dist
Now I want to implement the check at each segment of the line, but I'm stuck. Any advice?
This is what I thought of, though it doesn't want to work:
def distance_check():
for i in range(len(current_line)-1):
if dist_point_to_line(current_line[i], current_line[i+1], dot_pos) < 10:
return True #dot allowed to be placed
return False
If dot_pos is a list then you have to get the last element of the list (dot_pos[-1]). Note the arguments to the function dist_point_to_line are single dots, rather than a list of dots:
def distance_check():
for i in range(len(current_line)-1):
if dist_point_to_line(current_line[i], current_line[i+1], dot_pos[-1]) < 10:
return True #dot allowed to be placed
return False

Creating a 2D billiard ball game, Ball velocity problem

I am trying to create a simple program for a billiard game where two balls (a) and (b) having radius (R) collides. I created a python program and its like this.
from math import sqrt, atan2, sin, cos, pi, inf
from numpy import array
W = 600 # width of the table
H = 300 # height of the table
R = 10 # the radius of the ball
A = 0 # deceleration constant
dt = 10 ** -3
ma = mb = 1 # masses of the particles a and b
def vec_magnitude(V1):
return sqrt(V1[0]**2 + V1[1]**2)
def collision_test(V1, V2):
if vec_magnitude(V1 - V2) <= 2 * R:
return True
def dot_product(V1, V2):
return sum(V1 * V2)
def after_collision_velocity(Va, Vb, Ra, Rb):
''' the equation that produces the velocity of the objects after the collision'''
Va_new = Va - ((2 * mb * dot_product(Va - Vb, Ra - Rb)) /
((ma + mb) * (vec_magnitude(Ra - Rb))**2) * (Ra - Rb))
Vb_new = Vb - ((2 * ma * dot_product(Vb - Va, Rb - Ra)) /
((ma + mb) * (vec_magnitude(Rb - Ra))**2) * (Rb - Ra))
return Va_new, Vb_new
def motion(P, V_mag, angle, V):
'''describes the motion of the ball'''
if P[1] < R: #reflection from top
P += array([0, 2 * (R - P[1])])
angle *= -1 #reflection from the angular perspective
return P, V_mag, angle, V
if P[0] < R: # reflection from left
P += array([2 * (R - P[0]), 0])
angle = pi - angle
return P, V_mag, angle, V
if P[1] > H - R: #reflection from bottom
P += array([0, 2 * (H - R - P[1])])
angle *= -1
return P, V_mag, angle, V
if P[0] > W - R: #reflection from right
P += array([2 * (W - R - P[0]), 0])
angle = pi - angle
return P, V_mag, angle, V
else:
V_mag -= A * dt
Vx = V_mag * cos(angle)
Vy = V_mag * sin(angle)
P += array([Vx * dt, Vy * dt])
V = array([Vx, Vy])
return P, V_mag, angle, V
file = open("test_drawing.txt", "w")
for line in open("tex.txt", "r"):
t = 0 # starting time
Xa, Ya, Xb, Yb, Vxa, Vya, Vxb, Vyb = [
int(i) for i in (line.rstrip()).split(" ")]
Pa = array([Xa, Ya], dtype=float) #position vector of the ball a
Pb = array([Xb, Yb], dtype=float) #position vector of the ball b
Va = array([Vxa, Vya], dtype=float) #velocity vvector of the ball a
Vb = array([Vxb, Vyb], dtype=float) #velocity vector of the ball b
Va_mag = vec_magnitude(Va)
Vb_mag = vec_magnitude(Vb)
if Vxa == 0: #these steps are necessarry to eliminate error on the angle process
Vxa = inf
angle_a = atan2(Vya, Vxa) # angle between velocity components of the ball a
if Vxb == 0:
Vxb = inf
angle_b = atan2(Vyb, Vxb) # angle between velocity components of the ball b
while t <= 10:
Pa, Va_mag, angle_a, Va = motion(Pa, Va_mag, angle_a, Va) #moving the ball a
Pb, Vb_mag, angle_b, Vb = motion(Pb, Vb_mag, angle_b, Vb) #moving the ball b
if collision_test(Pa, Pb) == True: #checking the collision validity
Va, Vb = after_collision_velocity(Va, Vb, Pa, Pb)
Va_mag = vec_magnitude(Va) #restating the velocities
Vb_mag = vec_magnitude(Vb)
if Va[0] == 0:
Va[0] = inf
angla_a = atan2(Va[1], Va[0]) #restating the angles
if Vb[0] == 0:
Vb[0] = inf
angle_b = atan2(Vb[1], Vb[0])
t += dt #incrementing time
file.write(str(Pa[0]) + " " + str(Pa[1]) + " " + str(Pb[0]) + " " + str(Pb[1]) + "\n")
print(Pa[0], Pa[1], Pb[0], Pb[1])
file.close()
when I draw a picture for a simple collision, which the data file would contain, (the input data)
100 200 140 200 4 4 -4 4 Values,
I get something like
I used this program to draw
from pylab import plot, show
Xa = [100] #X component of the ball a
Ya = [200] #Y component of the ball a
Xb = [140] #X compnonent of the ball b
Yb = [200] $ Y component of the ball b
for line in open("test_drawing.txt", "r"):
data = [float(i) for i in line.split(" ")]
Xa.append(data[0])
Ya.append(data[1])
Xb.append(data[2])
Yb.append(data[3])
plot(Xa, Ya, "-r")
plot(Xb, Yb, "-.g")
show()
As you can see, ball (b) bounces but not the ball (a). To determine the velocity I used the equation in wikipedia page of the elastic collision.
https://en.wikipedia.org/wiki/Elastic_collision
Can anyone understand why this happens ?
You have a typo.
angla_a = atan2(Va[1], Va[0]) #restating the angles
should say angle_a. You are never actually updating angle_a after a collision.

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

Categories

Resources