I am given 2 points inside a circle. I have to find a point on the circle (not inside, not outside) so that the sum of the distances between the given 2 points and the point i found is minimum. I only have to find the minimum distance, not the position of the point.
It is a minimization problem:
minimize sqrt((x1-x0)^2 + (y1-y0)^2) + sqrt((x2-x0)^2 + (y2-y0)^2)
^ ^
(distance from point1) (distance from point 2)
subject to constraints:
x1 = C1
y1 = C2
x2 = C3
x4 = C4
x0^2 + y0^2 = r^2
(assuming the coordinates are already aligned to the center of the circle as (0,0)).
(C1,C2,C3,C4,r) are given constants, just need to assign them.
After assigning x1,y1,x2,y2 - you are given a minimization problem with 2 variables (x0,y0) and a constraint. The minimization problem can be solved using lagrange multiplier.
Let (x1, y1) and (x2, y2) be the points inside the circle, (x, y) be the point on the circle, r be the radius of the circle.
Your problem reduces to a Lagrange conditional extremum problem, namely:
extremum function:
f(x, y) = sqrt((x-x1)^2 + (y-y1)^2) + sqrt((x-x2)^2 + (y-y2)^2)
condition:
g(x, y) = x^2 + y^2 = r^2 (1)
Introduce an auxiliary function(reference):
Λ(x, y, λ) = f(x, y) + λ(g(x, y) - r^2)
Let ∂Λ / ∂x = 0, we have:
(x-x1)/sqrt((x-x1)^2 + (y-y1)^2) + (x-x2)/sqrt((x-x2)^2 + (y-y2)^2) + 2λx = 0 (2)
Let ∂Λ / ∂y = 0, we have:
(y-y1)/sqrt((x-x1)^2 + (y-y1)^2) + (y-y2)/sqrt((x-x2)^2 + (y-y2)^2) + 2λy = 0 (3)
Now we have 3 variables(i.e. x, y and λ) and 3 equations(i.e. (1), (2) and (3)), so it's solvable.
Please be noted that there should be two solutions(unless two inside points happen to be the center of the circle). One is the minimal value(which is what you need), the other is the maximal value(which will be ignored).
just another approach(which is more directly):
for simplicity, assume the circle is at (0,0) with radius r
and suppose two points are P1(x1,y1) and P2(x2,y2)
we can calculate the polar angle of these two points, suppose they are alpha1 and alpha2
obviously, the point which is on the circle and having the minimum sum of distance to P1 and P2 is within the circular sector composed of alpha1 and alpha2
meanwhile, the sum of distance between the point on the circle within that sector and P1 and P2 is a quadratic function. so, the minimum distance can be found by using trichotomy.
Related
For a given line of an equation y = mx + c where m is the gradient and c is the y-intercept. How would I determine the "edge-points" on a graph?
To clarify what I mean by "edge-points", I've added an example below.
The edge-points are circled in red. To determine the edge-points here it would simply be (0, c) & (maximum x-value, m * maximum x-value + c). However, the problem arises when I consider lines with a different m value. For example:
I can't apply the same logic here, instead the edge-points would be ((maximum y-value - c) * m, maximum y-value) & (-c * m, 0), which was derived from the equation y = mx + c. So my question is how would I determine these 2 edge-points given any m or c? Is there a certain pattern I'm not seeing here?
You need to solve 4 simple linear equations (not equation system!):
y = m * 0 + c = c
y = m * maxX + c
0 = m * x + c
maxY = m * x + c
and get points of intersections with axes and with max lines. Then filter out points with negative coordinates and too large ones, because you want only the 1st quadrant
The first one equation is already solved y=c
The second gives point of intersection with right vertical line
The third gives point of intersection with OX axis
The fourth gives point of intersection with top horizontal line
Example:
maxX = 5
maxY = 5
line y = 2 *x - 1
x0, y0 = 0, -1
x1, y1 = 5, 9
x2, y2 = 1/2, 0
x3, y3 = 2, 5
First pair contains negative coordinate y=-1
Second pair contains y=9 > maxY
Third and fourth ones fulfill your constraints.
So this line gives segment (1/2, 0)-(2, 5) (like near vertical segment at your second picture)
This algo might be considered as simple kind of line clippping by rectangle
For the line to be in the given rectangle there is a constraint given by the x values and a constraint given by the y values.
The x constraint trivially leads to an interval in which the x values must be.
The y constraint also gives you such an interval for the x values but only after some easy calculation.
Now determine the intersection of the two intervals (which may also be empty).
In my iOS app I draw a bezier spline through a set of 2D points using an algorithm that calculates set of first and second control points for each bezier curve joining two consecutive points.
The algorithm works brilliantly but the my problem is that now I am displaying this curve on a graph with X and Y axes.
Whenever there are couple of points or on the axes, and rest of them inside, the curve seems to take a U-turn and go beyond the axis and come back in two go through both the points and the ones that are inside.
From the algorithm perspective there is nothing wrong n that. Controls point in such case will be generated out side the coordinate axes.
Is there a way that I can go through the points and see if some of them are on the axes and modify the controls points such that the curve instead of going outside the curve just passes through both points on the axis as a straight line coinciding with coordinate axis.
Detecting a curve that goes off-screen
Bezier curves are actually just polynomial equations. There are multiple types of Bezier curves, corresponding to the differing degrees of the curve.
So, just take the equations that are generated (one for x, one for y), and find the roots (points where they pass through 0). If the roots exist, the curve passes the axis.
For example, for a quadratic (degree=2) Bezier curve, given our three points (x0, y0), (x1, y1), (x2, y2) the equations are
x(t) = (x0 - 2x1 + x2)t2 - 2(x0 + x1)t + x0
y(t) = (y0 - 2y1 + y2)t2 - 2(y0 + y1)t + y0
0 <= t <= 1
At this point, we could use the quadratic formula to solve for t, and check that the solution either doesn't exist or is outside our domain of [0,1] for t. However, there is another method that generalizes for cubic Bezier curves, without having to touch the complicated cubic formula - just check the extrema-points. These are the points where the derivative = 0. Since the derivative is
x'(t) = 2(x0 - 2x1 + x2)t - 2(x0 + x1)
y'(t) = 2(y0 - 2y1 + y2)t - 2(y0 + y1)
This means the extrema are at
textrema-x = (x0 + x1) / (x0 - 2x1 + x2)
textrema-y = (y0 + y1) / (y0 - 2y1 + y2)
Thus, just check that x(textrema-x) > 0 and y(textrema-y) > 0 (We are assuming the endpoints x0 and x2 are known to be on the screen aka > 0). For cubic Bezier curves, do the same thing, but use the quadratic-formula to solve for the extremas (there are two extremas for each of x and y in that case).
Preventing the curve from going off-screen
If we solve for x(textrema-x) > 0 by taking our equation above for textrema-x and plugging it into x(t), we find after a bit of algebra that, to prevent crossing the x-axis, we need
-4x0x1 + x0x2 - x12 > 0 if (x0 + x2) > 2x1
-4x0x1 + x0x2 - x12 < 0 if (x0 + x2) < 2x1
(if (x0 + x2) = 2x1, we are moving linearly from x0 to x2 so, assuming both are positive, we can't cross the x-axis)
A bit of staring at these equations should convince you that if (x0 + x2) > 2x1, we can simply decrease x1 until the lefthand condition is met; and if (x0 + x2) < 2x1, we can simply increase x1 until the condition is met. The equations for y are analogous.
This tells us how to prevent the curve from going off the left (x=0) and top (y=0) portions of the screen - what about the right (x=screen_width) and bottom (y=screen_height)?
We can do this easily by imagining we are flipping the screen to its mirror-image, so that the right-side is x=0 and the left-side is x=screen_width. This can be done by replacing every instance of x with (screen_width-x) in all equations above, and checking our conditions. Then, for example, when before in the first case we'd decrease x1, in this case we'd want to decrease (screen_width-x1), which is the same as increasing x1. The same logic holds for replacing y with (screen_height-y).
I'm searching the way to efficiently find the point on an edge which is the closest point to some other point.
Let's say I know two points which are vertices of the edge. I can calculate the equation of the line that crosses those points.
What is the best way to calculate the point on the edge which is the closest point to some other point in the plane.
I would post an image but I don't have enough reputation points.
Let’s assume the line is defined by the two points (x1,y1), (x2,y2) and the “other point” is (a,b).
The point you’re looking for is (x,y).
You can easily find the equation of the black line. To find the blue line equation use the fact that m1*m2=-1 (m1 and m2 are the slopes of the two lines).
Clearly, the point you’re looking for is the intersection between the two lines.
There are two exceptions to what I was saying:
If x1=x2 then (x,y)=(x1,b).
If y1=y2 then (x,y)=(a,y1).
The following Python function finds the point (if you don’t know Python just think of it as a psudo-code):
def get_closest_point( x1,y1, x2,y2, a,b ):
if x1==x2: return (x1,b)
if y1==y2: return (a,y1)
m1 = (y2-y1)/(x2-x1)
m2 = -1/m1
x = (m1*x1-m2*a+b-y1) / (m1-m2)
y = m2*(x-a)+b
return (x,y)
You have three zones to consider. The "perpendicular" approach is for the zone in the middle:
For the other two zones the distance is the distance to the nearest segment endpoint.
The equation for the segment is:
y[x] = m x + b
Where
m -> -((Ay - By)/(-Ax + By)),
b -> -((-Ax By + Ay By)/(Ax - By))
And the perpendiculars have slope -1/m
The equations for the perpendicular passing thru A is:
y[x] = (-Ax + By)/(Ay - By) x + (Ax^2 + Ay^2 - Ax By - Ay By)/(Ay - By)
And the perpendicular passing thru B is the same exchanging the A's and B's in the equation above.
So you can know in which region lies your point introducing its x coordinate in the above equations and then comparing the y coordinate of the point with the result of y[x]
Edit
How to find in which region lies your point?
Let's suppose Ax ≤ Bx (if it's the other way, just change the point labels in the following formulae)
We will call your point {x0,y0}
1) Calculate
f[x0] = (-Ax + By)/(Ay - By) x0 + (Ax^2 + Ay^2 - Ax By - Ay By)/(Ay - By)
and compare with y0.
If y0 > f[x0], then your point lies in the green field in the figure above and the nearest point is A.
2) Else, Calculate
g[x0] = (-Bx + Ay)/(By - Ay) x0 + (Bx^2 + By^2 - Bx Ay - By Ay)/(By - Ay)
and compare with y0.
If y0 < g[x0], then your point lies in the yellow field in the figure above and the nearest point is B.
3) Else, you are in the "perpendicular light blue zone", and any of the other answer tell you how to calculate the nearest point and distance (I am not going to plagiarize :))
HTH!
I can describe what you want to do in geometric terms, but I don't have the algorithm at hand. Will that help?
Anyway, you want to draw a line which contains the stray point and is perpendicular to the edge. I think the slopes are a negative inverse relation between perpendicular lines, if that helps.
Then you want to find the intersection of the two lines.
Let's stick with the 2D case to save typing. It's been a while, so please forgive any elementary mistakes in my algebra.
The line forming the edge between the two points (x1, y1), (x2, y2) is represented as a function
y = mx + b
(You get to figure out m and b yourself, but it's elementary)
What you want to do is minimize the distance from your point (p1, p2) to a point on this line, i.e.
(p1-x)^2 + (p2-y)^2 (equation I)
subject to the equation
y = mx + b (equation II)
Substitute equation II into equation I and solve for x. You'll get two solutions; pick the one which gives the smaller value in equation I.
For a square grid the euclidean distance between tile A and B is:
distance = sqrt(sqr(x1-x2)) + sqr(y1-y2))
For an actor constrained to move along a square grid, the Manhattan Distance is a better measure of actual distance we must travel:
manhattanDistance = abs(x1-x2) + abs(y1-y2))
How do I get the manhattan distance between two tiles in a hexagonal grid as illustrated with the red and blue lines below?
I once set up a hexagonal coordinate system in a game so that the y-axis was at a 60-degree angle to the x-axis. This avoids the odd-even row distinction.
(source: althenia.net)
The distance in this coordinate system is:
dx = x1 - x0
dy = y1 - y0
if sign(dx) == sign(dy)
abs(dx + dy)
else
max(abs(dx), abs(dy))
You can convert (x', y) from your coordinate system to (x, y) in this one using:
x = x' - floor(y/2)
So dx becomes:
dx = x1' - x0' - floor(y1/2) + floor(y0/2)
Careful with rounding when implementing this using integer division. In C for int y floor(y/2) is (y%2 ? y-1 : y)/2.
I assume that you want the Euclidean distance in the plane between the centers of two tiles that are identified as you showed in the figure. I think this can be derived from the figure. For any x and y, the vector from the center of tile (x, y) to the center of tile (x + dx, y) is (dx, 0). The vector from the center of tile (x, y) and (x, y + dy) is (-dy / 2, dy*sqrt(3) / 2). A simple vector addition gives a vector of (dx - (dy / 2), dy * sqrt(3) / 2) between (x, y) and (x + dx, y + dy) for any x, y, dx, and dy. The total distance is then the norm of the vector: sqrt((dx - (dy / 2)) ^ 2 + 3 * dy * dy / 4)
If you want the straight-line distance:
double dy = y2 - y1;
double dx = x2 - x1;
// if the height is odd
if ((int)dy & 1){
// whether the upper x coord is displaced left or right
// depends on whether the y1 coordinate is odd
dx += ((y1 & 1) ? -0.5 : 0.5);
}
double dis = sqrt(dx*dx + dy*dy);
What I'm trying to say is, if dy is even, it's just a rectangular space. If dy is odd, the position of the upper right corner is 1/2 unit to the left or to the right.
A straight forward answer for this question is not possible. The answer of this question is very much related to how you organize your tiles in the memory. I use odd-q vertical layout and with the following matlab code gives me the right answer always.
function f = offset_distance(x1,y1,x2,y2)
ac = offset_to_cube(x1,y1);
bc = offset_to_cube(x2,y2);
f = cube_distance(ac, bc);
end
function f = offset_to_cube(row,col)
%x = col - (row - (row&1)) / 2;
x = col - (row - mod(row,2)) / 2;
z = row;
y = -x-z;
f = [x,z,y];
end
function f= cube_distance(p1,p2)
a = abs( p1(1,1) - p2(1,1));
b = abs( p1(1,2) - p2(1,2));
c = abs( p1(1,3) - p2(1,3));
f = max([a,b,c]);
end
Here is a matlab testing code
sx = 6;
sy = 1;
for i = 0:7
for j = 0:5
k = offset_distance(sx,sy,i,j);
disp(['(',num2str(sx),',',num2str(sy),')->(',num2str(i),',',num2str(j),')=',num2str(k)])
end
end
For mathematical details of this solution visit: http://www.redblobgames.com/grids/hexagons/ . You can get a full hextile library at: http://www.redblobgames.com/grids/hexagons/implementation.html
This sounds like a job for the Bresenham line algorithm. You can use that to count the number of segments to get from A to B, and that will tell you the path distance.
If you define the different hexagons as a graph, you can get the shortest path from node A to node B. Since the distance from the hexagon centers is constant, set that as the edge weight.
This will probably be inefficient for large fields though.
I am trying to determine whether a line segment (i.e. between two points) intersects a sphere. I am not interested in the position of the intersection, just whether or not the segment intersects the sphere surface. Does anyone have any suggestions as to what the most efficient algorithm for this would be? (I'm wondering if there are any algorithms that are simpler than the usual ray-sphere intersection algorithms, since I'm not interested in the intersection position)
If you are only interested if knowing if it intersects or not then your basic algorithm will look like this...
Consider you have the vector of your ray line, A -> B.
You know that the shortest distance between this vector and the centre of the sphere occurs at the intersection of your ray vector and a vector which is at 90 degrees to this which passes through the centre of the sphere.
You hence have two vectors, the equations of which fully completely defined. You can work out the intersection point of the vectors using linear algebra, and hence the length of the line (or more efficiently the square of the length of the line) and test if this is less than the radius (or the square of the radius) of your sphere.
I don't know what the standard way of doing it is, but if you only want to know IF it intersects, here is what I would do.
General rule ... avoid doing sqrt() or other costly operations. When possible, deal with the square of the radius.
Determine if the starting point is inside the radius of the sphere. If you know that this is never the case, then skip this step. If you are inside, your ray will intersect the sphere.
From here on, your starting point is outside the sphere.
Now, imagine the small box that will fit sphere. If you are outside that box, check the x-direction, y-direction and z-direction of the ray to see if it will intersect the side of the box that your ray starts at. This should be a simple sign check, or comparison against zero. If you are outside the and moving away from it, you will never intersect it.
From here on, you are in the more complicated phase. Your starting point is between the imaginary box and the sphere. You can get a simplified expression using calculus and geometry.
The gist of what you want to do is determine if the shortest distance between your ray and the sphere is less than radius of the sphere.
Let your ray be represented by (x0 + it, y0 + jt, z0 + kt), and the centre of your sphere be at (xS, yS, zS). So, we want to find t such that it would give the shortest of (xS - x0 - it, yS - y0 - jt, zS - z0 - kt).
Let x = xS - x0, y = yX - y0, z = zS - z0, D = magnitude of the vector squared
D = x^2 -2*xit + (i*t)^2 + y^2 - 2*yjt + (j*t)^2 + z^2 - 2*zkt + (k*t)^2
D = (i^2 + j^2 + k^2)t^2 - (xi + yj + zk)*2*t + (x^2 + y^2 + z^2)
dD/dt = 0 = 2*t*(i^2 + j^2 + k^2) - 2*(xi + yj + z*k)
t = (xi + yj + z*k) / (i^2 + j^2 + k^2)
Plug t back into the equation for D = .... If the result is less than or equal the square of the sphere's radius, you have an intersection. If it is greater, then there is no intersection.
This page has an exact solution for this problem. Essentially, you are substituting the equation for the line into the equation for the sphere, then computes the discriminant of the resulting quadratic. The values of the discriminant indicate intersection.
Are you still looking for an answer 13 years later? Here is a complete and simple solution
Assume the following:
the line segment is defined by endpoints as 3D vectors v1 and v2
the sphere is centered at vc with radius r
Ne define the three side lengths of a triangle ABC as:
A = v1-vc
B = v2-vc
C = v1-v2
If |A| < r or |B| < r, then we're done; the line segment intersects the sphere
After doing the check above, if the angle between A and B is acute, then we're done; the line segment does not intersect the sphere.
If neither of these conditions are met, then the line segment may or may not intersect the sphere. To find out, we just need to find H, which is the height of the triangle ABC taking C as the base. First we need φ, the angle between A and C:
φ = arccos( dot(A,C) / (|A||C|) )
and then solve for H:
sin(φ) = H/|A|
===> H = |A|sin(φ) = |A| sqrt(1 - (dot(A,C) / (|A||C|))^2)
and we are done. The result is
if H < r, then the line segment intersects the sphere
if H = r, then the line segment is tangent to the sphere
if H > r, then the line segment does not intersect the sphere
Here that is in Python:
import numpy as np
def unit_projection(v1, v2):
'''takes the dot product between v1, v2 after normalization'''
u1 = v1 / np.linalg.norm(v1)
u2 = v2 / np.linalg.norm(v2)
return np.dot(u1, u2)
def angle_between(v1, v2):
'''computes the angle between vectors v1 and v2'''
return np.arccos(np.clip(unit_projection(v1, v2), -1, 1))
def check_intersects_sphere(xa, ya, za, xb, yb, zb, xc, yc, zc, radius):
'''checks if a line segment intersects a sphere'''
v1 = np.array([xa, ya, za])
v2 = np.array([xb, yb, zb])
vc = np.array([xc, yc, zc])
A = v1 - vc
B = v2 - vc
C = v1 - v2
if(np.linalg.norm(A) < radius or np.linalg.norm(B) < radius):
return True
if(angle_between(A, B) < np.pi/2):
return False
H = np.linalg.norm(A) * np.sqrt(1 - unit_projection(A, C)**2)
if(H < radius):
return True
if(H >= radius):
return False
Note that I have written this so that it returns False when either endpoint is on the surface of the sphere, or when the line segment is tangent to the sphere, because it serves my purposes better.
This might be essentially what user Cruachan suggested. A comment there suggests that other answers are "too elaborate". There might be a more elegant way to implement this that uses more compact linear algebra operations and identities, but I suspect that the amount of actual compute required boils down to something like this. If someone sees somewhere to save some effort please do let us know.
Here is a test of the code. The figure below shows several trial line segments originating from a position (-1, 1, 1) , with a unit sphere at (1,1,1). Blue line segments have intersected, red have not.
And here is another figure which verifies that line segments that stop just short of the sphere's surface do not intersect, even if the infinite ray that they belong to does:
Here is the code that generates the image:
import matplotlib.pyplot as plt
radius = 1
xc, yc, zc = 1, 1, 1
xa, ya, za = xc-2, yc, zc
nx, ny, nz = 4, 4, 4
xx = np.linspace(xc-2, xc+2, nx)
yy = np.linspace(yc-2, yc+2, ny)
zz = np.linspace(zc-2, zc+2, nz)
n = nx * ny * nz
XX, YY, ZZ = np.meshgrid(xx, yy, zz)
xb, yb, zb = np.ravel(XX), np.ravel(YY), np.ravel(ZZ)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(n):
if(xb[i] == xa): continue
intersects = check_intersects_sphere(xa, ya, za, xb[i], yb[i], zb[i], xc, yc, zc, radius)
color = ['r', 'b'][int(intersects)]
s = [0.3, 0.7][int(intersects)]
ax.plot([xa, xb[i]], [ya, yb[i]], [za, zb[i]], '-o', color=color, ms=s, lw=s, alpha=s/0.7)
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = np.outer(np.cos(u), np.sin(v)) + xc
y = np.outer(np.sin(u), np.sin(v)) + yc
z = np.outer(np.ones(np.size(u)), np.cos(v)) + zc
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='k', linewidth=0, alpha=0.25, zorder=0)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.tight_layout()
plt.show()
you sorta have to work that the position anyway if you want accuracy. The only way to improve speed algorithmically is to switch from ray-sphere intersection to ray-bounding-box intersection.
Or you could go deeper and try and improve sqrt and other inner function calls
http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection