Related
Given center S, radius r and angle alpha, How can I find the coordinates of interceptions between Line-Circle (the line is given by a point(always center of the circle) and angle).The line is endless, so there are always 2 interceptions. It has to be a general formula/code.
The result are the coordinates.
How can I do that?
Imagine rotating the line angle =1 and getting all the points of a circle.
Circle-Line Intersection from https://mathworld.wolfram.com/ did not work for me because my line is defined by a point and an angle.
The solution I ended up with:
x1 = centerx + R * cos(alpha)
y1 = centery + R * sin(alpha)
x2 = centerx - R * cos(alpha)
y2 = centery - R * sin(alpha)
I am coding a game in Unity that number of your soldiers are increasing/decreasing by some triggers. I want to position my soldier objects like a full circle, so they will always be near each other(like same distances) even if their number is increasing or decreasing. How can I manage this?
You can start with some simple relatively ordered distribution of positions and by applying a dynamical system approach/gradient decent type iteration, you can let the positions converge to a much more structured pattern. I wrote such implementation in python, it is in vectorized form, but I also added an equivalent function with for loops, to illustrate the structure of the function. The final ordered pattern is inspired by the stable equilibrium position that a bunch of discs of same radius r would form if they are hold by springs, one for every two of them. To ease up the computations, I squared the spring tensions, thus avoiding square roots, so not exactly like the typical physics model, but close to it.
import numpy as np
import matplotlib.pyplot as plt
def Grad(pos, r):
Dq = - pos[:, :, np.newaxis] + pos[:, np.newaxis, :]
D = Dq[0,:,:]*Dq[0,:,:] + Dq[1,:,:]*Dq[1,:,:] + np.identity(Dq.shape[1])
Dq = (1 - r**2 / D) * Dq
return - Dq.sum(axis=2)
def Grad_flow(q_, r, step):
Q = q_
n_iter = 0
while True:
n_iter = n_iter + 1 # you can count the number of iterations needed to reach the equilibrium
Q_prev = Q
Q = Q - step * Grad(Q, r)
if np.sum(np.abs((Q.T).dot(Q) - (Q_prev.T).dot(Q_prev))) < 1e-5:
return Q
'''
Test:
'''
p = np.array([[-3, 3], [-1, 3], [1,3], [3,3],
[-3, 1], [-1, 1], [1,1], [3,1],
[-3,-1], [-1,-1], [1,-1], [3,-1],
[-3,-3], [-1, -3], [1, -3], [3,-3],
[-2, 1], [-1,2],[2,-2], [-2,-2],
[2,2], [2,0]]).T
r = 0.5
step = 0.01
q = Grad_flow(p, r, step)
'''
Plot:
'''
fig, axs = plt.subplots(1,1)
axs.set_aspect('equal')
axs.plot(q[0,:], q[1,:], 'ro')
axs.plot(p[0,:], p[1,:], 'bo')
plt.grid()
plt.show()
You start from the blue positions and you make them converge to the red positions:
Here is the loop version of the Grad function:
def Grad(pos, r):
grad = np.zeros(pos.shape, dtype=float)
for i in range(pos.shape[1]):
for j in range(pos.shape[1]):
if not i==j:
d_pos_0 = pos[0, i] - pos[0, j]
d_pos_1 = pos[1, i] - pos[1, j]
m = d_pos_0*d_pos_0 + d_pos_1*d_pos_1
m = 1 - r*r / m
grad[0, i] = grad[0, i] + m * d_pos_0
grad[1, i] = grad[1, i] + m * d_pos_1
return grad
Of course, all of this is a bit heuristic and I cannot promise full generality, so you have to play and select the parameters r which is half-distance between positions, iteration step-size step, the initial position p and so on.
Supposing you are working in the horizontal plane, you can define you much rotation for each of your soldiers, and the find that point in the plane converting cartesian coordinates (x, y) into polar ones (R, fi), add theta to fi and then convert back to cartesian:
// Rotate B around A by angle theta
private (float x, float y) Rotate(
(float x, float y) A,
(float x, float y) B,
float theta) {
float fi = Math.Atan2(B.y - A.y, B.x - A.x) + theta;
float R = Math.Sqrt((A.y - B.y) * (A.y - B.y) + (A.x - B.x) * (A.x - B.x));
return (A.x + R * Math.Cos(fi), A.y + R * Math.Sin(fi));
}
Another option that does exactly the same thing, but not using polar coords:
// Rotate B around A by angle theta clockwise
private (float x, float y) Rotate(
(float x, float y) A,
(float x, float y) B,
float theta)
{
float s = Math.Sin(theta);
float c = Math.Cos(theta);
// translate point back to origin:
B.x -= A.x;
B.y -= A.y;
// rotate point clockwise
float xnew = B.x * c - B.y * s;
float ynew = B.x * s + B.y * c;
// translate point back:
B.x = xnew + A.x;
B.y = ynew + A.y;
return B;
}
If you want your soldiers equally distributed in a circle you would need to calcualte the rotation angle of each just with float angle = 360 / numSoldiers;.
If your game is in 3d and you are working in the floor plane (XZ) you can change the .ys by .zs in the code.
You can also check how the algorithms work in a simple unity project cubes or in a console c# app to understand them and to check how they just perform the rotation of a vector's end point around its origin to return the rotated point. I think that is what you would need to find the points of interest for the position of your soldiers.
I have an axis-aligned rectangle in a 2d coordinate system represented by the point on the lower left and the point on the upper right, as well as a point which might be inside or outside of the rectangle. I want to find the distance of the point to the nearest point of the rectangle, regardless of whether it is inside the rectangle or not. Of course I could just write a switch case with 9 different outcomes but I am hoping that there is a more elegant solution.
Also, I have found multiple solutions to this problem (like this one), but all of them would calculate the distance as 0 if the point is inside the box, which I don't want.
My answer is slightly longer than the others, but it comes from a different perspective.
The key isn't if you're inside the rectangle, but if you're anywhere within the corridors defined by taking the sides of the rectangle and extending them infinitely (picture an infinite plus sign, centered on the rectangle).
If it's inside those corridors, then the closest distance is orthogonal to one of the sides.
If it's outside, then the closest distance is the distance to the nearest corner.
Your code could look like this:
nearest_distance(rectangle, point):
d_top = abs(rectangle.top - point.y)
d_bottom = abs(rectangle.bottom - point.y)
corner_y = d_top < d_bottom ? rectangle.top : rectangle.bottom
d_left = abs(rectangle.left - point.x)
d_right = abs(rectangle.right - point.x)
corner_x = d_left < d_right ? rectangle.left : rectangle.right
d_cx = corner_x - point.x
d_cy = corner_y - point.y
d_corner = sqrt(d_cx*d_cx + d_cy*d_cy)
return min(d_top, d_bottom, d_left, d_right, d_corner)
If you wanted to try to save a sqrt, you could check if you're inside the corridors vs. outside of them. In that case, you would rearrange it as follows:
nearest_distance(rectangle, point):
d_top = abs(rectangle.top - point.y)
d_bottom = abs(rectangle.bottom - point.y)
d_left = abs(rectangle.left - point.x)
d_right = abs(rectangle.right - point.x)
r = rectangle # just to make the next line neater
if r.left <= point.x <= r.right or r.bottom <= point.y <= r.top:
return min(d_top, d_bottom, d_left, d_right)
else:
corner_y = d_top < d_bottom ? rectangle.top : rectangle.bottom
corner_x = d_left < d_right ? rectangle.left : rectangle.right
d_cx = corner_x - point.x
d_cy = corner_y - point.y
d_corner = sqrt(d_cx*d_cx + d_cy*d_cy)
return d_corner
You could extend the linked solution of MultiRRomero and do some additional computations for points inside the rectangle.
For these points the closest point on the boundary of the rectangle has the same x or y coordinate as the point. So computing the distances to the lines is straight forward and the smallest will be the desired distance.
function distance(rect, p) {
var dx = Math.max(rect.min.x - p.x, 0, p.x - rect.max.x);
var dy = Math.max(rect.min.y - p.y, 0, p.y - rect.max.y);
var distance = Math.sqrt(dx*dx + dy*dy)
if (distance == 0) {
distance = Math.min(p.x - rect.min.x, rect.max.x - p.x, p.y - rect.min.y, rect.max.y - p.y)
}
return distance
}
Edit: typos fixed
How about something like this? (first part "stolen" from MultiRRomeros answer)
function distance(rect, p) {
// outside
var dxo = Math.max(rect.min.x - p.x, 0, p.x - rect.max.x);
var dyo = Math.max(rect.min.y - p.y, 0, p.y - rect.max.y);
var hypothenuse = Math.sqrt(dxo*dxo + dyo*dyo);
// inside
var dxi = Math.min(rect.max.x - p.x, p.x - rect.min.x);
var dyi = Math.min(rect.max.y - p.y, p.y - rect.min.y);
return hypothenuse > 0 ? hypothenuse : Math.min(dxi, dyi);
}
Assume that we have a rectangle or a square and we know the x,y coordinates of its corners (4 corners).
Also assume that we have a point inside that square for which we know its coordinates (x,y), its speed (km/h), its heading (heading is measured in directional degrees, 0 for north, 180 for south and so on) and the time point it has these attributes (epoch time in seconds).
How can we calculate the time point (epoch time in seconds) in which the point will exit the rectangle as well as the coordinates (x,y) of the exit ?
You need to find what edge is intersected first. Make equations for moving along both coordinates and calculate the first time of intersection.
Note that for geographic coordinates you might need more complex calculations because "rectangle" defined by Lat/Lon coordinates is really curvy trapezoid on the Earth surface. Look at "Intersection of two paths given start points and bearings" chapter on this page to get travel time.
vx = V * Cos(heading + Pi/2) //for y-axis north=0
vy = V * Sin(heading + Pi/2)
x = x0 + vx * t
y = y0 + vy * t
//potential border positions
if vx > 0 then
ex = x2
else
ex = x1
if vy > 0 then
ey = y2
else
ey = y1
//check for horizontal/vertical directions
if vx = 0 then
return cx = x0, cy = ey, ct = (ey - y0) / vy
if vy = 0 then
return cx = ex, cy = y0, ct = (ex - x0) / vx
//in general case find times of intersections with horizontal and vertical edge line
tx = (ex - x0) / vx
ty = (ey - y0) / vy
//and get intersection for smaller parameter value
if tx <= ty then
return cx = ex, cy = y0 + tx * vy, ct = tx
else
return cx = x0 + ty * vx, cy = ey, ct = ty
I've googled till I'm blue in the face, and unless I'm missing something really obvious, I can't find any algorithms for calculating the bounding box of a 2D sector.
Given the centre point of the enclosing circle, the radius, and the angles of the extent of the sector, what's the best algorithm to calculate the axis-aligned bounding rectangle of that sector?
Generate the following points:
The circle's center
The positions of the start and end angles of the sector
Additionally, for the angles among 0, 90, 180, and 270 that are within the angle range of the sector, their respective points on the sector
Calculate the min and max x and y from the above points. This is your bounding box
I'm going to rephrase yairchu's answer so that it is clearer (to me, anyway).
Ignore the center coordinates for now and draw the circle at the origin. Convince yourself of the following:
Anywhere the arc intersects an axis will be a max or a min.
If the arc doesn't intersect an axis, then the center will be one corner of the bounding rectangle, and this is the only case when it will be.
The only other possible extreme points of the sector to consider are the endpoints of the radii.
You now have at most 4+1+2 points to find. Find the max and min of those coordinates to draw the rectangle.
The rectangle is easily translated to the original circle by adding the coordinates of the center of the original circle to the rectangle's coordinates.
First of all I apologize if I commit mistakes writing but english is not my first language, spanish is actually!
I faced this problem, and I think I found an efficient solution.
First of all let's see an image of the situation
So we have an ellipse (actually a circle) and two points (C, D) which indicates our sector.
We also have the center of our circle (B) and the angle of the Arc alpha.
Now, in this case I made it passing through 360º on porpouse to see if it would work.
Let's say alpha -> -251.1º (it negative cause its clockwise), lets transform it to positive value 360º - 251.1º = 108.9º now our goal is to find the angle of the bisection of that angle so we can find the max point for the bounding box (E in the image), actually as you may have realized, the length of the segment BE equals the radius of the circle but we must have the angle to obtain the actual coordinates of the E point.
So 108.9º / 2 -> 54.45º now we have the angle.
To find the coordinates of E we use polar coordinates so
x = r * Cos(theta)
y = r * Sin(theta)
we have r and theta so we can calculate x and y
in my example r = 2.82… (actually it's irational but I took the first two decimal digits as a matter of ease)
We know our first radii is 87.1º so theta would be 87.1 - 54.45º -> 32.65º
we know *theta * is 32.65º so let's do some math
x = 2.82 * Cos(32.65º) -> 2.37552
y = 2.82 * Sin(32.65º) -> 1.52213
Now we need to adjust these values to the actual center of the circle so
x = x + centerX
y = y + centerY
In the example, the circle is centered at (1.86, 4.24)
x -> 4.23552
y -> 5.76213
At this stage we should use some calculus. We know that one of the edges of the bounding box will be a tangent of the arc that passes through the point we just calculated so, lets find that tangent (the red line).
We know that the tangent passes through our point (4.23, 5.76) now we need a slope.
As you can see, the slope is the same as the slope of the rect that passes through our radii's so we have to find that slope.
For doing that we need to get the coordinates of our radii's (a fast conversion to cartessian coordinates from polar coordinates).
x = r * Cos(theta)
y = r * Sin(theta)
So
p0 = (centerX + 2.82 * Cos(87.1º), centerY + 2.82 * Sin(87.1º))
p1 = (centerX + 2.82 * Cos(-21.8º), centerY + 2.82 * Sin(-21.8º))
(21.8º is the angle measured clockwise from the horizontal axis to the radii that is below it and thus I put it negative)
p0 (2, 7.06)
p1 (4.48, 3.19)
now let's find the slope:
m = (y - y0) / (x - x0)
...
m = (3.19 - 7.06) / (4.48-2) = -3.87 / 2.48 = -1.56048
...
m = -1.56
having the slope we need to calculate the equation for the tangent, basically is a rect with an already known slope (m = -1.56) that passes through an already know point (E -> (4.23, 5.76))
So we have Y = mx + b where m = -1.56, y = 5.76 and x = 4.23 so b must be
b = 5.76 - (-1.56) * 4.23 = 12.36
Now we have the complete equation for our tangent -> Y = -1.56X + 12.36
All we must do know is project the points C and D over that rect.
We need the equations for the rects CH and DI so let's calculate 'em
Let's start with CH:
We know (from the tanget's equation) that our direction vector is (1.56, 1)
We need to find a rect that passes through the point C -> (2, 7.06)
(x - 2) / 1.56 = (y - 7.06) / 1
Doing some algebra -> y = 0.64x + 5.78
We know have the equation for the rect CH we must calculate the point H.
we have to solve a linear system as follows
y = -1.56x + 12.36
y = 1.56x + 5.78
Solving this we'll find the point H (3, 7.69)
We need to do the same with the rect DI so let's do it
Our direction vector is (1.56, 1) once again
D -> (4.48, 3.19)
(x - 4.48) / 1.56 = (y -3.19) / 1
Doing some algebra -> y = 0.64x + 0.32
Lets solve the linear system
y = -1.56x + 12.36
y = 0.64x + 0.32
I (5.47, 3.82)
At this stage we already have the four points that make our Bounding box -> C, H, D , I
Just in case you don't know or rememeber how to solve a linear system on a programming language, i'll give you a little example
It's pure algebra
Let's say we have the following system
Ax + By = C
Dx + Ey = F
then
Dx = F - Ey
x = (F - Ey) / D
x = F/D - (E/D)y
replacing on the other equation
A(F/D - (E/D)y) + By = C
AF/D - (AE/D)y + By = C
(AE/D)y + By = C - AF/D
y(-AE/D + B) = C - AF/D
y = (C - AF/D) / (-AE/D + B)
= ( (CD - AF) / D ) / ( (-AE + BD) / D) )
so
y = (CD - AF) / (BD - AE)
and for x we do the same
Dx = F - Ey
Dx - F = -Ey
Ey = F - Dx
y = F/E - (D/E)x
replacing on the other equation
Ax + B(F/E - (D/E)x) = C
Ax + (BF/E - (DB/E)x) = C
Ax - (DB/E)x = C - BF/E
x (A-(DB/E)) = C - BF/E
x = (C - BF/E)/(A-(DB/E))
= ((CE - BF) / E) / ((AE-DB) / E)
x = (CE - BF) / (AE - DB)
I apologize for the extent of my answer but I meant to be as clear as possible and thus, I made it almost step by step.
In C# code:
/// <summary>
/// The input parameters describe a circular arc going _clockwise_ from E to F.
/// The output is the bounding box.
/// </summary>
public Rect BoundingBox(Point E, Point F, Point C, double radius)
{
// Put the endpoints into the bounding box:
double x1 = E.X;
double y1 = E.Y;
double x2 = x1, y2 = y1;
if (F.X < x1)
x1 = F.X;
if (F.X > x2)
x2 = F.X;
if (F.Y < y1)
y1 = F.Y;
if (F.Y > y2)
y2 = F.Y;
// Now consider the top/bottom/left/right extremities of the circle:
double thetaE = Math.Atan2(E.Y - C.Y, E.X - C.X);
double thetaF = Math.Atan2(F.Y - C.Y, F.X - C.X);
if (AnglesInClockwiseSequence(thetaE, 0/*right*/, thetaF))
{
double x = (C.X + radius);
if (x > x2)
x2 = x;
}
if (AnglesInClockwiseSequence(thetaE, Math.PI/2/*bottom*/, thetaF))
{
double y = (C.Y + radius);
if (y > y2)
y2 = y;
}
if (AnglesInClockwiseSequence(thetaE, Math.PI/*left*/, thetaF))
{
double x = (C.X - radius);
if (x < x1)
x1 = x;
}
if (AnglesInClockwiseSequence(thetaE, Math.PI*3/2/*top*/, thetaF))
{
double y = (C.Y - radius);
if (y < y1)
y1 = y;
}
return new Rect(x1, y1, x2 - x1, y2 - y1);
}
/// <summary>
/// Do these angles go in clockwise sequence?
/// </summary>
private static bool AnglesInClockwiseSequence(double x, double y, double z)
{
return AngularDiffSigned(x, y) + AngularDiffSigned(y, z) < 2*Math.PI;
}
/// <summary>
/// Returns a number between 0 and 360 degrees, as radians, representing the
/// angle required to go clockwise from 'theta1' to 'theta2'. If 'theta2' is
/// 5 degrees clockwise from 'theta1' then return 5 degrees. If it's 5 degrees
/// anticlockwise then return 360-5 degrees.
/// </summary>
public static double AngularDiffSigned(double theta1, double theta2)
{
double dif = theta2 - theta1;
while (dif >= 2 * Math.PI)
dif -= 2 * Math.PI;
while (dif <= 0)
dif += 2 * Math.PI;
return dif;
}
I tried to implement jairchu's answer, but found some problems, which I would like to share:
My coordinate system for the circle starts with 0 degrees at the right side of the circle and runs counterclockwise through the top (90deg), the left(180deg) and the bottom (270deg). The angles can be between 0 and 359,9999 deg.
The center point should not be part of the list of points
You have to distinguish between clockwise and counterclockwise arcs in order to make the list of points that lie on 0,90,180,270 deg
It is tricky to determine if the angle span includes the angle 0,90,180 or 270 deg.
public override Rect Box()
{
List<Point> potentialExtrema = new List<Point>();
potentialExtrema.Add(StartPoint);
potentialExtrema.Add(EndPoint);
if (!ClockWise)
{
if (EndAngle < StartAngle || EndAngle == 0 || StartAngle == 0 || EndAngle == 360 || StartAngle == 360)
potentialExtrema.Add(new Point(Point.X + Radius, Point.Y));
if ((StartAngle <= 90 || StartAngle > EndAngle) && EndAngle >= 90)
potentialExtrema.Add(new Point(Point.X, Point.Y + Radius));
if ((StartAngle <= 180 || StartAngle > EndAngle) && EndAngle >= 180)
potentialExtrema.Add(new Point(Point.X - Radius, Point.Y));
if ((StartAngle <= 270 || StartAngle > EndAngle) && EndAngle >= 270)
potentialExtrema.Add(new Point(Point.X, Point.Y - Radius));
}
else
{
if (StartAngle < EndAngle || EndAngle == 0 || StartAngle == 0 || EndAngle == 360 || StartAngle == 360)
potentialExtrema.Add(new Point(Point.X + Radius, Point.Y));
if ((StartAngle >= 90 || StartAngle < EndAngle) && EndAngle <= 90)
potentialExtrema.Add(new Point(Point.X, Point.Y + Radius));
if ((StartAngle >= 180 || StartAngle < EndAngle) && EndAngle <= 180)
potentialExtrema.Add(new Point(Point.X - Radius, Point.Y));
if ((StartAngle >= 270 || StartAngle < EndAngle) && EndAngle <= 270)
potentialExtrema.Add(new Point(Point.X, Point.Y - Radius));
}
double maxX = double.NegativeInfinity;
double maxY = double.NegativeInfinity;
double minX = double.PositiveInfinity;
double minY = double.PositiveInfinity;
foreach (var point in potentialExtrema)
{
if (point.X > maxX)
maxX = point.X;
if (point.Y > maxY)
maxY = point.Y;
if (point.X < minX)
minX = point.X;
if (point.Y < minY)
minY = point.Y;
}
return new Rect(minX, minY, maxX - minX, maxY - minY);
}
}
There is a more elegant solution determining wether 0,90,180 or 270 deg lie within the angle span:
public override Rect Box()
{
List<Point> potentialExtrema = new List<Point>();
potentialExtrema.Add(StartPoint);
potentialExtrema.Add(EndPoint);
if (AngleProduct(0))
potentialExtrema.Add(new Point(Point.X + Radius, Point.Y));
if (AngleProduct(90))
potentialExtrema.Add(new Point(Point.X, Point.Y + Radius));
if (AngleProduct(180))
potentialExtrema.Add(new Point(Point.X - Radius, Point.Y));
if (AngleProduct(270))
potentialExtrema.Add(new Point(Point.X, Point.Y - Radius));
double maxX = double.NegativeInfinity;
double maxY = double.NegativeInfinity;
double minX = double.PositiveInfinity;
double minY = double.PositiveInfinity;
foreach (var point in potentialExtrema)
{
if (point.X > maxX)
maxX = point.X;
if (point.Y > maxY)
maxY = point.Y;
if (point.X < minX)
minX = point.X;
if (point.Y < minY)
minY = point.Y;
}
return new Rect(minX, minY, maxX - minX, maxY - minY);
}
private bool AngleProduct(int alpha)
{
if (StartAngle == EndAngle)
if (StartAngle == alpha)
return true;
else
return false;
double prod = 0;
if (ClockWise)
prod = -1 * (alpha - StartAngle) * (EndAngle - alpha) * (EndAngle - StartAngle);
else
prod = (alpha - StartAngle) * (EndAngle - alpha) * (EndAngle - StartAngle);
if (prod >= 0)
return true;
else
return false;
}