Map coordinates of an area in Python - python

A bit of a tricky question but I hope you can help me out.
I've received a coding assignment and I'm having some trouble working my way around to a solution.
The situation is this: suppose I have a weapon in coordinates (X,Y); When it fires, a sound damage function kicks in that affects people in close proximity to the gun. I need to write a function that will calculate - in accordance to the caliber being fired - whether or not a person (an instance of a Person class) is present in the dangerous area. The sound from the gun spreads out from a 45-degree angle and has a radius that changes in accordance to the caliber being fired (but is predetermined by me, so let's assume the radius equals to 5).
My explanation might be a little messy, so I'll try to organize it a little to make things clearer: I need to write a function that is called whenever a gun is being fired. The function needs to check whether or not a person is in dangerously close proximity to the gun being fired as if he is he will be inflicted with damage to his hearing (by a separate, pre-made function). The damage scales in accordance with the person's relative distance from the gun. The area is measured in a cone that spreads out the edge of the gun in a 45 degree angle in a random radius.
I'm a little confused as to how to tackle this problem. I have already written a function that calculates the coordinates of each "Person". But I need a way to map out all the coordinates of the impact area so that I can a) tell whether the person should be affected by the sound-damage function and b) tell how badly he was hit (as the damage would be worse if he was standing 1 meter away from the gun as opposed to 4 meters away).
Maybe I'm approaching the problem in a wrong way - I want to learn and am happy to listen to any suggestions. I'm no expert by any means and would love to maybe get some directions as to how to approach this problem in the most efficient way.
Edit: The code I've written so far is the following:
class Soldier(object):
def __init__(self):
# Soldier ID is a randomly generated string including of 3 numbers.
ID = str(int(random.random() * 1000))
self.ID = ID
# soldier's position is set by default to [0,0]
self.position = [0,0]
def getID(self):
# gets the ID number (string) of the instance
print (self.ID)
def setPosition(self, position):
# sets the position (a list) of the instance
self.position = position
def getPosition(self):
# gets the position (list) of the instance
print(self.position)
def randomize_soldier_position(soldiersList):
"""
Gets as input a list of instances of soldiers, and assigns a random location to each one of the instances.
"""
for i in soldiersList:
# for the values of X and Y we generate a random floating point number extending to 3 digits.
X = round((random.random() * 100), 3)
Y = round((random.random()* 100), 3)
# sets the [X,Y] values for each of the instances.
i.setPosition([X,Y])
print(i.position)
I haven't touched the code of the actual function I need to write as I have no idea where to even begin...
Thanks in advance! :)

assuming you have a Euclidean 2D coordinate grid and angle reference
the gun should have a x,y position and a angle that it is pointing relative to the global coordinate axis
if the gun has a direction vector instead of an angle just apply math.atan2(x, y), which will return the angle in radians
subtract the gun x, y from the person global x,y position to get a gun to person relative vector, calculate the angle with atan2, the distance with Pythagoras math.hypot(x, y)
then just test if the person is within the angle range and distance range

Related

Taking the coordinates of an object and creating a formula to drag an arrow

I am using OpenCV to triangulate the position of an object, and am trying to create some kind of formula to pass the coordinates that I obtain through to drag a pull arrow, casting a fishing rod. I tried using polynomial regression to a very high degree, but it is still inaccurate due to the regression not being able to take into account an (x,y) input to an (x,y) output, rather just an x input to x output etc. I have attached screenshots below for clarity, alongside my obtained formulas from the regression. Any help/ideas/suggestions would be appreciated, thanks.
Edit:
The xy coordinates are organized from the landing position to the position where the arrow was pulled to for the bobber to land there. This is because the fishing blob is the input, and the arrow pull end location comes from the blob location. I am using OpenCV to obtain the x,y coordinates, which I believe is just an x,y coordinate system of the 2d screen.
The avatar position is locked, and the button to cast the rod is located at an absolute position of (957,748).
The camera position is locked with no rotation or movement.
I believe that the angle the rod is cast at is likely a 1:1 opposite of where it is pulled to. Ex: if the rod was pulled to 225 degrees it would cast at 45 degrees. I am not 100% sure, but I think that the strength is linear. I used linear regression partially because I was not sure about this. There is no altitude difference/slope/wind that affects the cast. The only affecting factor of landing position is where the arrow is dragged to. The arrow will not drag past the 180/360 degree position sideways (relative to cast button) and will simply lock the cast angle in the x direction if it is held there.
The x-y data was collected with a simple program to move the mouse to the same position (957,748) and drag the arrow to cast the rod with different drag strengths/positions to create some kind of line of best fit for a general formula for casting the rod. The triang_x and y functions included are what the x and y coordinates were run through respectively to triangulate the ending drag coordinate for the arrow. This does not work very well because matching the x-to-x and y-to-y doesn't account for x and y data in each formula, just x-to-x etc.
Left column is fishing spot coordinates, right column is where arrow is dragged to to hit the fish spot.
(1133,359) to (890,890)
(858,334) to (886, 900)
(755,579) to (1012,811)
(1013,255) to (933,934)
(1166,469) to (885,855)
(1344,654) to (855,794)
(804,260) to (1024,939)
(1288,287) to (822,918)
(624,422) to (1075,869)
(981,460) to (949,851)
(944,203) to (963,957)
(829,367) to (1005,887)
(1129,259) to (885,932)
(773,219) to (1036,949)
(1052,314) to (919,908)
(958,662) to (955,782)
(1448,361) to (775,906)
(1566,492) to (751,837)
(1275,703) to (859,764)
(1210,280) to (852,926)
(668,513) to (1050,836)
(830,243) to (1011,939)
(688,654) to (1022,792)
(635,437) to (1072,864)
(911,252) to (976,935)
(1499,542) to (785,825)
(793,452) to (1017,860)
(1309,354) to (824,891)
(1383,522) to (817,838)
(1262,712) to (867,758)
(927,225) to (980,983)
(644,360) to (1097,919)
(1307,648) to (862,798)
(1321,296) to (812,913)
(798,212) to (1026,952)
(1315,460) to (836,854)
(700,597) to (1028,809)
(868,573) to (981,811)
(1561,497) to (758,838)
(1172,588) to (896,816)
Shows bot actions taken within function and how formula is used.
coeffs_x = np.float64([
-7.9517089428836911e+005,
4.1678460255861210e+003,
-7.5075555590709371e+000,
4.2001528427460097e-003,
2.3767929866943760e-006,
-4.7841176483548307e-009,
6.1781765539212100e-012,
-5.2769581174002655e-015,
-4.3548777375857698e-019,
2.5342561455214514e-021,
-1.4853535063513160e-024,
1.5268121610772846e-027,
-2.9667978919426497e-031,
-9.5670287721717018e-035,
-2.0270490020866057e-037,
-2.8248895597371365e-040,
-4.6436110892973750e-044,
6.7719507722602512e-047,
7.1944028726480678e-050,
1.2976299392064562e-052,
7.3188205383162127e-056,
-6.3972284918241943e-059,
-4.1991571617797430e-062,
2.5577340340980386e-066,
-4.3382682133956009e-068,
1.5534384486024757e-071,
5.1736875087411699e-075,
7.8137258396620031e-078,
2.6423817496804479e-081,
2.5418438527686641e-084,
-2.8489136942892384e-087,
-2.3969101111450846e-091,
-3.3499890707855620e-094,
-1.4462592756075361e-096,
6.8375394909274851e-100,
-2.4083095685910846e-103,
7.0453288171977301e-106,
-2.8342463921987051e-109
])
triang_x = np.polynomial.Polynomial(coeffs_x)
coeffs_y = np.float64([
2.6215449742035207e+005,
-5.7778572049616614e+003,
5.1995066291482431e+001,
-2.3696608508824663e-001,
5.2377319234985116e-004,
-2.5063316505492962e-007,
-9.2022083686040928e-010,
3.8639053124052189e-013,
2.7895763914453325e-015,
7.3703786336356152e-019,
-1.3411964395287408e-020,
1.5532055573746500e-023,
-6.9719956967963252e-027,
1.9573598517734802e-029,
-3.3847482160483597e-032,
-5.5368209294319872e-035,
7.1463648457003723e-038,
4.6713369979545088e-040,
-7.5070219026265008e-043,
-4.5089676791698693e-047,
-3.2970870269153785e-049,
1.6283636917056585e-051,
-1.4312555782661719e-054,
7.8463441723355399e-058,
1.9439588820918080e-060,
2.1292310369635749e-063,
-1.4191866473449773e-065,
-2.1353539347524828e-070,
2.5876946863828411e-071,
-1.6182477348921458e-074
])
triang_y = np.polynomial.Polynomial(coeffs_y)
First you need to clarify few things:
the xy data
Is position of object you want to hit or position what you hit when used specific input data (which is missing in that case)?In what coordinate system?
what position is your avatar?
how is the view defined?
is it fully 3D with 6DOF or just fixed (no rotation or movement) relative to avatar?
what is the physics/logic of your rod casting
is it angle (one or two), strength?Is the strength linear to distance?Does throwing acount for altitude difference between avatar and target?does ground elevation (slope) play a role?Are there any other factors like wind, tupe of rod etc?
You shared the xy data but what against you want to correlate or make formula for it? it does not make sense you obviously forget to add something like each position was taken for what conditions?
I would solve this by (no further details before you clarify stuff above):
transform targets xy to player relative coordinate system aligned to ground
compute azimut angle (geometricaly)
simple atan2(y,x) will do but you need to take into account your coordinate system notations.
compute elevation angle and strength (geometricaly)
simple balistic physics should apply however depends on the physics the game or whatever you write this for uses.
adjust for additional stuff
You know for example wind can slightly change your angle and strength
In case you have real physics and data you can do #3,#4 at the same time. See similar:
C++ intersection time of 2 bullets
[Edit1] puting your data into your image
OK your coordinates obviously do not match your screenshot as the image taken is scaled after some intuition I rescaled it and draw into image in C++ to match again so here the result:
I converted your Cartesian points:
int ava_x=957,ava_y=748; // avatar
int data[]= // target(x1,y1) , drag(x0,y0)
{
1133,359,890,890,
858,334,886, 900,
755,579,1012,811,
1013,255,933,934,
1166,469,885,855,
1344,654,855,794,
804,260,1024,939,
1288,287,822,918,
624,422,1075,869,
981,460,949,851,
944,203,963,957,
829,367,1005,887,
1129,259,885,932,
773,219,1036,949,
1052,314,919,908,
958,662,955,782,
1448,361,775,906,
1566,492,751,837,
1275,703,859,764,
1210,280,852,926,
668,513,1050,836,
830,243,1011,939,
688,654,1022,792,
635,437,1072,864,
911,252,976,935,
1499,542,785,825,
793,452,1017,860,
1309,354,824,891,
1383,522,817,838,
1262,712,867,758,
927,225,980,983,
644,360,1097,919,
1307,648,862,798,
1321,296,812,913,
798,212,1026,952,
1315,460,836,854,
700,597,1028,809,
868,573,981,811,
1561,497,758,838,
1172,588,896,816,
};
Into polar relative to ava_x,ava_y using atan2 and 2D distance formula and simply print the angular difference +180deg and ratio between line sizes (that is the yellow texts in left of the screenshot) first is ordinal number then angle difference [deg] and then ratio between line lengths...
as you can see the angle difference is +/-10.6deg and length ratio is <2.5,3.6> probably because of inaccuracy of OpenCV findings and some randomness for fishing rod castings from the game logic itself.
As you can see polar coordinates are best for this. For starters you could do simply this:
// wanted target in polar (obtained by CV)
x = target_x-ava_x;
y = target_y-ava_y;
a = atan2(y,x);
l = sqrt((x*x)+(y*y));
// aiming drag in polar
a += 3.1415926535897932384626433832795; // +=180 deg
l /= 3.0; // "avg" ratio between line sizes
// aiming drag in cartesian
aim_x = ava_x + l*cos(a);
aim_y = ava_y + l*sin(a);
You can optimize it to:
aim_x = ava_x - ((target_x-ava_x)/3);
aim_y = ava_y - ((target_y-ava_y)/3);
Now to improve precision you could measure the dependency or line ratio and line size (it might be not linear) , also the angular difference might be bigger for bigger lines ...
Also note that second cast (ordinal 2) is probably a bug (wrongly detected x,y by CV) if you render the 2 lines you will see they do not match so you should not account that and throw them away from dataset.
Also note that I code in C++ so my goniometrics use radians (not sure if true for python if not you need to convert to degrees) also equations might need some additional tweaking for your coordinate systems (negate y?)

Random Walk of a Photon Through the Sun

For a project, I am trying to determine the time it would take for a photon to leave the Sun. However, I am having trouble with my code (found below).
More specifically, I set up a for loop with an if statement, and if some randomly generated probability is less than the probability of collision, that means the photon collides and it changes direction.
What I am having trouble with is setting up a condition where the for loop stops if the photon escapes (when distance > Sun radius). The one I have set up already doesn't appear to work.
I use a very scaled down measurement of the Sun's radius because if I didn't it would take a long time for the photon to escape in my simulation.
from numpy.random import random as rng # we want them random numbers
import numpy as np # for the math functions
import matplotlib.pyplot as plt # to make pretty pretty class
mass_proton = 1.67e-27
mass_electron = 9.11e-31
Thompson_cross = 6.65e-29
Sun_density = 150000
Sun_radius = .005
Mean_Free = (mass_proton + mass_electron)/(Thompson_cross*Sun_density*np.sqrt(2))
time_step= 10**-13 # Used this specifically in order for the path length to be < Mean free Path
path_length = (3e8)*time_step
Probability = 1-np.exp(-path_length/Mean_Free) # Probability of the photon colliding
def Random_walk():
x = 0 # Start at origin (0,0)
y = 0
N = 1000
m=0 # This is a counter I have set up for the number of collisions
for i in range(1,N+1):
prand = rng(N+1) # Randomly generated probability
if prand[i] < Probability: # If my prand is less than the probability
# of collision, the photon collides and changes
# direction
x += Mean_Free*np.cos(2*np.pi*prand)
y += Mean_Free*np.sin(2*np.pi*prand)
m += 1 # Everytime a collision occurs 1 is added to my collision counter
distance = np.sqrt(x**2 + y**2) # Final distance the photon covers
if np.all(distance) > Sun_radius: # if the distance the photon travels
break # is greater than the Radius of the Sun,
# the for loop stops, meaning the
#photon escaped
print(m)
return x,y,distance
x,y,d = Random_walk()
plt.plot(x,y, '--')
plt.plot(x[-1], y[-1], 'ro')
Any criticisms of my code are welcome, this is for a grade and I do want to learn how to do this correctly, so please tell me if you notice any other errors.
I don't understand the motivation for the formulas you've implemented. I'll explain my own motivation here, but if your instructor told you to do something else, I guess you should listen to them instead.
If I were going to do this, I would generate a sequence of movements of a photon, stopping when distance of the photon to the center of the sun is greater than the solar radius. Each movement is a sample from a distribution which has two components: one for the distance, and one for the direction. I will assume that these are independent (this may be questioned in a more careful simulation).
It seems plausible that the distribution of distance is an exponential distribution with parameter 1/(mean free path). Then the density is p(d) = (1/MFP) exp(-d/MFP). Its cdf is 1 - exp(-d/MFP) and the inverse of the cdf is -MFP log(1 - p) where p = cdf(d). Now you can sample from the distribution of distances: let p = rand(0, 1) where rand = uniform random and plug it into the inverse cdf to get d. This is called the inverse cdf method of sampling; a web search will find more info about it.
As for the direction, you can let angle = rand(0, 2*pi) and then (x, y) = (cos(angle), sin(angle)).
Now you can construct the series of positions. From an initial location, let the new location = previous + d*(x, y). Stop when distance of location to center is greater than radius.
Looks like a great problem! Good luck and have fun. Let me know if you have any questions.
Here is a way of thinking about the problem that you may find helpful. At each moment, the photon has a position (x, y) and a direction (dx, dy). The (dx, dy) variables are coefficients of the unit vector, so sqrt(dx**2 + dy**2) = 1.0. The distance traveled by the photon during one step is path_length * direction.
At each step you do 4 things:
calculate the photon's new position
figure out if the photon has left the sun by computing its distance from the center point
determine, with a single random number, whether or not the photon collides. If it does you randomly generate a new direction.
Append the photon's current position to a list. You might want to do this as a function of distance from the center rather than x,y.
At the end, you plot the list you have built up.
You should also choose a random direction at the very start.
I don't know how you will terminate the loop, for the photon isn't ever guaranteed to leave the sun - just like in the real world. In principle the program might run forever (or until the sun burns out).
There is a slight inaccuracy in that the photon can collide at any instant, not just at the end of one step. But since the steps are small, so is the error introduced by this simplification.
I will point out that you do not need numpy for any of this except perhaps the final plot. The standard Python library has all the math functions you need. Numpy is of course great for manipulating arrays of data, but the only array you will have here is the one you build, a step at a time, of photon position versus time.
As I pointed out in one of my comments, you are modeling the sun as a 2-dimensional object. If you want to do this calculation in three dimensions, you don't need to change this basic approach.

Code to resemble a rowing stroke

I'm working on a small program displaying moving rowing boats. The following shows a simple sample code (Python 2.x):
import time
class Boat:
def __init__(self, pace, spm):
self.pace = pace #velocity of the boat in m/s
self.spm = spm #strokes per minute
self.distance = 0 #distance travelled
def move(self, deltaT):
self.distance = self.distance + (self.pace * deltaT)
boat1 = Boat(3.33, 20)
while True:
boat1.move(0.1)
print boat1.distance
time.sleep(0.1)
As you can see a boat has a pace and rows with a number of strokes per minute. Everytime the method move(deltaT) is called it moves a certain distance according to the pace.
The above boat just travels at a constant pace which is not realistic. A real rowing boat accelerates at the beginning of a stroke and then decelerates after the rowing blades left the water. There are many graphs online which show a typical rowing curve (force shown here, velocity looks similar):
Source: highperformancerowing.net
The pace should be constant over time, but it should change during the stroke.
What is the best way to change the constant velocity into a curve which (at least basically) resembles a more realistic rowing stroke?
Note: Any ideas on how to tag this question better? Is it an algorithm-problem?
If your goal is to simply come up with something visually plausible and not to do a full physical simulation, you can simply add a sine wave to the position.
class Boat:
def __init__(self, pace, spm, var=0.5):
self.pace = pace #average velocity of the boat in m/s
self.sps = spm/60.0 #strokes per second
self.var = var #variation in speed from 0-1
self.totalT = 0 #total time
self.distance = 0 #distance traveled
def move(self, deltaT):
self.totalT += deltaT
self.distance = self.pace * (self.totalT + self.var * math.sin(self.totalT * self.sps * 2*math.pi)
You need to be careful with the variation var, if it gets too high the boat might go backwards and destroy the illusion.
You can convert a curve like this into a polynomial equation for velocity.
A description/example of how to do this can be found at:
python numpy/scipy curve fitting
This shows you how to take a set of x,y coordinates (which you can get by inspection of your existing plot or from actual data) and create a polynomial function.
If you are using the same curve for each Boat object, you could just hard code it into your program. But you could also have a separate polynomial equation for each Boat object as well, assuming each rower or boat has a different profile.
You can perform simple integration of the differential equation of motion. (This is what you are already doing to get space as a function of time, with constant speed, x' = x + V.dt.)
Assume a simple model with a constant force during the stroke and no force during the glide, and drag proportional to the speed.
So the acceleration is a = P - D.v during stroke, and - D.v during glide (deceleration).
The speed is approximated with v' = v + a.dt.
The space is approximated with x' = x + v.dt.
If dt is sufficiently small, this motion should look realistic. You can refine the model with a more accurate force law and better integration techniques like Runge-Kutta, but I am not sure it is worth it.
Below an example plot of speed and space vs time using this technique. It shows speed oscillations quickly establishing a periodic regime, and quasi-linear displacement with undulations.

Point wrapping algorithm - A blocked Swinging door

I'm trying to some some code in python. Basically what it does is simulates a door (viewed from above) on an (x,y) coordinate system. The task is given a list of points, determine which the door will hit first, if any.
Determining if a point is within range to be hit by the door is simple enough, determining which point gets hit first is proving to be difficult, as the door can swing clockwise or counter clockwise, and has a rather large, and variable range of swing (in terms of radians/degrees). The issue is mostly that I'm not sure what conditions need to be true for the point to be hit first.
Update:
I do have the angles calculated, but concerned about special cases such as when the door is at 1 degree, and swinging clockwise towards points at angles 180, 190, and 300 for example.
Calculate the angle from the door hinge to each of the points; whichever is closest to the current angle of the door itself (hinge to door edge) will be hit first when rotating.
If the cycling is giving you trouble: notice that for any given angle, you can subtract it from 360 to get its complement; whichever is the smaller of the two is the closer way to get to it. So:
Calculate all angles for the points a1 ... aN
Subtract them all from the door angle to get difference angles d1...dN
Replace each dN with min( dN, 360 - dN ) to get the "shorter" approach
Pick the minimum
This can be simplified if you think in terms of the difference between the angle of door and the angle of each point relative to the hinge of the door.
You then find the angle with this formula:
length of vector from hinge to door: A
length of vector from hinge to point: B
angle = (A * B)/(A^2 + B^2)

Is a top-left origin coordinate system what's stopping this equation from working?

I am trying to implement this equation to determine the centre of a circle from three user-selected points: http://en.wikipedia.org/wiki/Circumscribed_circle#Cartesian_coordinates
First, the points are acquired and assembled into a list by means of this OpenCV mouse callback function:
def setupPoints(event, x, y, flags, points):
#Populates a provided list 'points' with
#three coordinates representing the edge of
#a circle
if points[0] == 0:
points[0] = (x,y)
elif points[1] == 0:
points[1] = (x,y)
else:
points[2] = (x,y)
Then I pass the list of points to this function, which does the work:
def findCircle(p):
#Returns the circle centre
#from three provided points provided as tuples
#in a list
#See http://en.wikipedia.org/wiki/Circumscribed_circle#Cartesian_coordinates
ax = float(p[0][0])
ay = float(p[0][1])
bx = float(p[1][0])
by = float(p[1][1])
cx = float(p[2][0])
cy = float(p[2][1])
d = 2*(ax*(by-cy)+bx*(cy-ay)+cx*(ay-by))
centrex = ((pow(ax,2)+pow(ay,2))*(by-cy)+(pow(bx,2)+pow(by,2))*(cy-ay)+(pow(cx,2)+pow(cy,2))*(ay-by))/d
centrey = ((pow(ax,2)+pow(ay,2))*(cx-bx)+(pow(bx,2)+pow(by,2))*(ax-cx)+(pow(cx,2)+pow(cy,2))*(bx-ax))/d
return (int(round(centrex)), int(round(centrey)), int(round(d)))
However, it's not working. The returned numbers aren't massively off, but they are definitely incorrect. Could this be to do with the fact that the coordinate system used by OpenCV has its origin in the top-left of the image (points within the image are still positive, so it could be said to be counting 'backwards', vertically at least).
Or is that guess wrong?
More than likely it's because the operands of your division are both integers, so the result is a (floored) integer. In Python, 2/3 == 0. This will throw off your calculations a little since they won't be rounded properly. Try dividing by float(d) rather than just d.
The answer to my question regarding whether the wonky (from a maths point of view, not a computer graphics points of view) coordinate system was preventing this from working is NO.
I simply forgot to add a check in my mouse callback function to make sure the event was a click and not simply a mouse movement; my points weren't what I was clicking!
Thanks for looking at this, maybe this will help someone in future...

Categories