Circle center : Cx,Cy
Circle radius : a
Point from which we need to draw a tangent line : Px,Py
I need the formula to find the two tangents (t1x, t1y) and (t2x,t2y) given all the above.
Edit: Is there any simpler solution using vector algebra or something, rather than finding the equation of two lines and then solving equation of two straight lines to find the two tangents separately? Also this question is not off-topic because I need to write a code to find this optimally
Here is one way using trigonometry. If you understand trig, this method is easy to understand, though it may not give the exact correct answer when one is possible, due to the lack of exactness in trig functions.
The points C = (Cx, Cy) and P = (Px, Py) are given, as well as the radius a. The radius is shown twice in my diagram, as a1 and a2. You can easily calculate the distance b between points P and C, and you can see that segment b forms the hypotenuse of two right triangles with side a. The angle theta (also shown twice in my diagram) is between the hypotenuse and adjacent side a so it can be calculated with an arccosine. The direction angle of the vector from point C to point P is also easily found by an arctangent. The direction angles of the tangency points are the sum and difference of the original direction angle and the calculated triangle angle. Finally, we can use those direction angles and the distance a to find the coordinates of those tangency points.
Here is code in Python 3.
# Example values
(Px, Py) = (5, 2)
(Cx, Cy) = (1, 1)
a = 2
from math import sqrt, acos, atan2, sin, cos
b = sqrt((Px - Cx)**2 + (Py - Cy)**2) # hypot() also works here
th = acos(a / b) # angle theta
d = atan2(Py - Cy, Px - Cx) # direction angle of point P from C
d1 = d + th # direction angle of point T1 from C
d2 = d - th # direction angle of point T2 from C
T1x = Cx + a * cos(d1)
T1y = Cy + a * sin(d1)
T2x = Cx + a * cos(d2)
T2y = Cy + a * sin(d2)
There are obvious ways to combine those calculations and make them a little more optimized, but I'll leave that to you. It is also possible to use the angle addition and subtraction formulas of trigonometry with a few other identities to completely remove the trig functions from the calculations. However, the result is more complicated and difficult to understand. Without testing I do not know which approach is more "optimized" but that depends on your purposes anyway. Let me know if you need this other approach, but the other answers here give you other approaches anyway.
Note that if a > b then acos(a / b) will throw an exception, but this means that point P is inside the circle and there is no tangency point. If a == b then point P is on the circle and there is only one tangency point, namely point P itself. My code is for the case a < b. I'll leave it to you to code the other cases and to decide the needed precision to decide if a and b are equal.
Here's another way using complex numbers.
If a is the direction (a complex number of length 1) of the tangent point on the circle from the centre c, and d is the (real) length along the tangent to get to p, then (because the direction of the tangent is I*a)
p = c + r*a + d*I*a
rearranging
(r+I*d)*a = p-c
But a has length 1 so taking the length we get
|r+I*d| = |p-c|
We know everything but d, so we can solve for d:
d = +- sqrt( |p-c|*|p-c| - r*r)
and then find the a's and the points on the circle, one of each for each value of d above:
a = (p-c)/(r+I*d)
q = c + r*a
Hmm not really an algorithm question (people tend to mistake algorithm and equation) If you want to write a code then do (you did not specify language nor what prevents you from doing this which is the reason of close votes)... Without this info your OP is just asking for math equation which is indeed off-topic here and by answering this I risk (right-full) down-votes too (but this is/was asked a lot here with much less info and 4 reopen votes against 1 close put my decision weight on reopen and answering this anyway).
You can exploit the fact that you are in 2D as in 2D perpendicular vectors to vector a(x,y) are computed like this:
c = (-y, x)
d = ( y,-x)
c = -d
so you swap x,y and negate one (which one determines if the perpendicular vector is CW or CCW). It is really a rotation formula but as we rotate by 90deg the cos,sin are just +1 and -1.
Now normal n to any circumference point on circle lies in the line going through that point and circles center. So putting all this together your tangents are:
// normal
nx = Px-Cx
ny = Py-Cy
// tangent 1
tx = -ny
ty = +nx
// tangent 2
tx = +ny
ty = -nx
If you want unit vectors than just divide by radius a (not sure why you do not call it r like the rest of the math world) so:
// normal
nx = (Px-Cx)/a
ny = (Py-Cy)/a
// tangent 1
tx = -ny
ty = +nx
// tangent 2
tx = +ny
ty = -nx
Let's go through derivation process:
As you can see, if the interior of the square is < 0 it's because the point is interior to the circumferemce. When the point is outside of the circumference there are two solutions, depending on the sign of the square.
The rest is easy. Take atan(solution) and be carefull here with the signs, you may better do some checks.
Use (2) and then undo (1) transformations and that's all.
c# implementation of dmuir's answer:
static void FindTangents(Vector2 point, Vector2 circle, float r, out Line l1, out Line l2)
{
var p = new Complex(point.x, point.y);
var c = new Complex(circle.x, circle.y);
var cp = p - c;
var d = Math.Sqrt(cp.Real * cp.Real + cp.Imaginary * cp.Imaginary - r * r);
var q = GetQ(r, cp, d, c);
var q2 = GetQ(r, cp, -d, c);
l1 = new Line(point, new Vector2((float) q.Real, (float) q.Imaginary));
l2 = new Line(point, new Vector2((float) q2.Real, (float) q2.Imaginary));
}
static Complex GetQ(float r, Complex cp, double d, Complex c)
{
return c + r * (cp / (r + Complex.ImaginaryOne * d));
}
Move the circle to the origin, rotate to bring the point on X and downscale by R to obtain a unit circle.
Now tangency is achieved when the origin (0, 0), the (reduced) given point (d, 0) and an arbitrary point on the unit circle (cos t, sin t) form a right triangle.
cos t (cos t - d) + sin t sin t = 1 - d cos t = 0
From this, you draw
cos t = 1 / d
and
sin t = ±√(1-1/d²).
To get the tangency points in the initial geometry, upscale, unrotate and untranslate. (These are simple linear algebra operations.) Notice that there is no need to perform the direct transform explicitly. All you need is d, ratio of the distance center-point over the radius.
Related
I have this problem I can't figure out and need help.
The problem is about calculating how many balloons are hit by a pellet gun. Balloons positions are described by 3D coordinates (X,Y,Z) and radius R. The gunshot is defined by 3D location of the end of the barrel "p" (Px,Py,Pz) and vector "v" (Vx, Vy, Vz) describing the direction barrel is pointing to.
I've tried to implement the solution suggested here: https://math.stackexchange.com/questions/1939423/calculate-if-vector-intersects-sphere
// C = center of sphere
// r = radius of sphere
// P = point on line
// U = unit vector in direction of line
Q = P - C;
a = U*U; // should be = 1
b = 2*U*Q
c = Q*Q - r*r;
d = b*b - 4*a*c; // discriminant of quadratic
if d < 0 then solutions are complex, so no intersections
if d >= 0 then solutions are real, so there are intersections
But the problem with this is that I get intersection with balloons that are positioned behind the gun. How can I modify this algorithm in order to produce the correct result? Or is my approach maybe wrong?
You need to actually solve the quadratic equation defined by your variables a, b and c.
Often, there are math libraries to do this, something like:
(t1,t2) = QuadraticSolve(a, b, c);
You can also do it manually for each parameter:
t1 = (-b + sqrt(b*b - 4*a*c)) / (2*a)
t2 = (-b - sqrt(b*b - 4*a*c)) / (2*a)
If t1 or t2 is positive then that intersection is in front of your gun.
Given the center, radius and and 3 points on a circle, I want to draw an arc that starts at the first point, passing through the second and ends at the third by specifying the angle to start drawing and the amount of angle to rotate. To do this, I need to calculate the points on the arc. I want the number of points calculated to be variable so I can adjust the accuracy of the calculated arc, so this means I probably need a loop that calculates each point by rotating a little after it has calculated a point. I've read the answer to this question Draw arc with 2 points and center of the circle but it only solves the problem of calculating the angles because I don't know how 'canvas.drawArc' is implemented.
This question has two parts:
How to find the arc between two points that passes a third point?
How to generate a set of points on the found arc?
Let's start with first part. Given three points A, B and C on the (O, r) circle we want to find the arc between A and C that passes through B. To find the internal angle of the arc we need to calculate the oriented angles of AB and AC arcs. If angle of AB was greater than AC, we are in wrong direction:
Va.x = A.x - O.x;
Va.y = A.y - O.y;
Vb.x = B.x - O.x;
Vb.y = B.y - O.y;
Vc.x = C.x - O.x;
Vc.y = C.y - O.y;
tb = orientedAngle(Va.x, Va.y, Vb.x, Vb.y);
tc = orientedAngle(Va.x, Va.y, Vc.x, Vc.y);
if tc<tb
tc = tc - 2 * pi;
end
function t = orientedAngle(x1, y1, x2, y2)
t = atan2(x1*y2 - y1*x2, x1*x2 + y1*y2);
if t<0
t = t + 2 * pi;
end
end
Now the second part. You said:
I probably need a loop that calculates each point by rotating a little
after it has calculated a point.
But the question is, how little? Since the perimeter of the circle increases as its radius increase, you cannot reach a fixed accuracy with a fixed angle. In other words, to draw two arcs with the same angle and different radii, we need a different number of points. What we can assume to be [almost] constant is the distance between these points, or the length of the segments we draw to simulate the arc:
segLen = someConstantLength;
arcLen = abs(tc)*r;
segNum = ceil(arcLen/segLen);
segAngle = tc / segNum;
t = atan2(Va.y, Va.x);
for i from 0 to segNum
P[i].x = O.x + r * cos(t);
P[i].y = O.y + r * sin(t);
t = t + segAngle;
end
Note that although in this method A and C will certainly be created, but point B will not necessarily be one of the points created. However, the distance of this point from the nearest segment will be very small.
I'm trying to code a general algorithm that can find a polygon from the area swept out by a circle (red line) that follows some known path (green line), and where the circle gets bigger as it moves further down the known path. Basically, can anyone point me down a direction to solve this, please? I can't seem to nail down which tangent points are part of the polygon for any point (and thus circle) on the path.
Any help is appreciated.
Well, the easiest is to approximate your path by small segments on which your path is linear, and your circle grows linearly.
Your segments and angles will likely be small, but for the sake of the example, let's take bigger (and more obvious) angles.
Going through the geometry
Good lines for the edges of your polygon are the tangents to both circles. Note that there aren't always close to the lines defined by the intersections between the circles and the orthogonal line to the path, especially with stronger growth speeds. See the figure below, where (AB) is the path, we want the (OE) and (OF) lines, but not the (MN) one for example :
The first step is to identify the point O. It is the only point that defines a homothetic transformation between both circles, with a positive ratio.
Thus ratio = OA/OB = (radius C) / (radius C') and O = A + AB/(1-ratio)
Now let u be the vector from O to A normalized, and v a vector orthogonal to u (let us take it in the direction from A to M).
Let us call a the vector from O to E normalized, and beta the angle EOA. Then, since (OE) and (AE) are perpendicular, sin(beta) = (radius C) / OA. We also have the scalar product a.u = cos(beta) and since the norm of a is 1, a = u * cos(beta) + v * sin(beta)
Then it comes easily that with b the vector from O to F normalized, b = u * cos(beta) - v * sin(beta)
Since beta is an angle less than 90° (otherwise the growth of the circle would be so much faster than it going forward, that the second circle contains the first completely), we know that cos(beta) > 0.
Pseudo-code-ish solution
For the first and last circles you can do something closer to them -- fort the sake of simplicity, I'm just going to use the intersection between the lines I'm building and the tangent to the circle that's orthogonal to the first (or last) path, as illustrated in the first figure of this post.
Along the path, you can make your polygon arbitrarily close to the real swept area by making the segments smaller.
Also, I assume you have a function find_intersection that, given two parametric equations of two lines, returns the point of intersection between them. First of all, it makes it trivial to see if they are parallel (which they should never be), and it allows to easily represent vertical lines.
w = 1; // width of the first circle
C = {x : 0, y : 0}; // first circle center
while( (new_C, new_w) = next_step )
{
// the vector (seg_x, seg_y) is directing the segment
seg = new_C - C;
norm_seg = sqrt( seg.x * seg.x + seg.y * seg.y );
// the vector (ortho_x, ortho_y) is orthogonal to the segment, with same norm
ortho = { x = -seg.y, y = seg.x };
// apply the formulas we devised : get ratio-1
fact = new_w / w - 1;
O = new_C - seg / fact;
sin_beta = w * fact / norm_seg;
cos_beta = sqrt(1 - sin_beta * sin_beta);
// here you know the two lines, parametric equations are O+t*a and O+t*b
a = cos_beta * seg + sin_beta * ortho;
b = cos_beta * seg - sin_beta * ortho;
if( first iteration )
{
// initialize both "old lines" to a line perpendicular to the first segment
// that passes through the opposite side of the circle
old_a = ortho;
old_b = -ortho;
old_O = C - seg * (w / norm_seg);
}
P = find_intersection(old_O, old_a, O, a);
// add P to polygon construction clockwise
Q = find_intersection(old_O, old_b, O, b);
// add Q to polygon construction clockwise
old_a = a;
old_b = b;
old_O = O;
w = new_w;
C = new_C;
}
// Similarly, finish with line orthogonal to last direction, that is tangent to last circle
O = C + seg * (w / norm_seg);
a = ortho;
b = -ortho;
P = find_intersection(old_O, old_a, O, a);
// add P to polygon construction clockwise
Q = find_intersection(old_O, old_b, O, b);
// add Q to polygon construction clockwise
Let's suppose the centers are along the positive x-axis, and the lines in the envelope are y=mx and y=-mx for some m>0. The distance from (x,0) to y=mx is mx/sqrt(1+m^2). So, if the radius is increasing at a rate of m/sqrt(1+m^2) times the distance moved along the x-axis, the enveloping lines are y=mx and y=-mx.
Inverting this, if you put a circle of radius cx at the center of (x,0), then c=m/sqrt(1+m^2) so
m = c/sqrt(1-c^2).
If c=1 then you get a vertical line, and if c>1 then every point in the plane is included in some circle.
This is how you can tell how much faster than sound a supersonic object is moving from the Mach angle of the envelope of the disturbed medium.
You can rotate this to nonhorizontal lines. It may help to use the angle formulation mu = arcsin(c), where mu is the angle between the envelope and the path, and the Mach number is 1/c.
I am trying to convert a movement along a straight line ( 2 points) to a movement along Hexagonal path, I tried different formula and did not work.
I would like to find out the coordinates of P,Q,R,M based on A and B.
I hope someone suggest a better formula which gives me the coordinates to move a long Hexagonal path.
If you are familiar with complex numbers (and assuming this is a regular hexagon),
D = B - A
P = A + D( 1 + sqrt(3)i )/4
Q = A + D( 3 + sqrt(3)i )/4
R = A + D( 1 - sqrt(3)i )/4
M = A + D( 3 - sqrt(3)i )/4
EDIT:
If you are not familiar with complex numbers, we should not attempt to use them here. They are a wonderful tool, but not easy to grasp at first. Let's do it the long way:
A = (Ax, Ay)
B = (Bx, By)
D = B - A = (Dx, Dy) where Dx=Ax-Bx and Dy=Ay-By
P = (Ax + Dx/4 - sqrt(3)Dy/4, Ay + Dy/4 + sqrt(3)Dx/4)
Q = (Ax + 3Dx/4 - sqrt(3)Dy/4, Ay + 3Dy/4 + sqrt(3)Dx/4)
R = (Ax + Dx/4 + sqrt(3)Dy/4, Ay + Dy/4 - sqrt(3)Dx/4)
M = (Ax + 3Dx/4 + sqrt(3)Dy/4, Ay + 3Dy/4 - sqrt(3)Dx/4)
This is easier to conceptualize if you imagine your hexagon as being made up of vectors - lines with a magnitude (distance) and a direction (angle from the west-to-east horizon rotating counterclockwise).
Call the vector from A to B D. If you use some trigonometry to figure out the geometry of a hexagon, D's magnitude is two times the length of the side of the hexagon. So, we can use this to construct vectors that are as large as our other hexagon sides, and thereby get the hexagon's other points.
Take the vector D, halve its magnitude, rotate it 60 degrees ccw and add this new vector to A's position. This gives you P.
Do the same thing but rotate it 60 degrees cw and add this to A's position. This gives you R.
Similarly, Q is the vector D halved, rotated 60 degrees cw, inverted and added to B's position.
Finally, M is the vector D halved, rotated 60 degrees ccw, inverted and added to B's position.
(To convert a vector into x distance moved and y distance moved, multiply the magnitude by the cos of the angle and by the sin of the angle respectively. Make sure you are using radians if radians are needed and degrees if degrees are needed.)
The following is what I am trying to figure out.
Question - Explain how you can project a 2D point onto a plane to create a 3D point.
I want to know how I would go about figuring this out. I have looked through a computational geometry book and looked for anything that may relate to what I'm trying to figure out. There was no information given along with the question about the computational geometry problem. The thing is I don't know anything about computational geometry< so figuring this out is beyond my knowledge.
Can anyone point me in the right direction?
If I understood this correctly you want to project points on the 2D plane onto a plane with a different orientation. I’m also going to assume that you are looking for the orthogonal projection (i.e. all points from the xy-plane are to be projected onto the closest point on the target plane).
So we have the equations of the two planes and the point we want to project:
The original 2D plane: z = 0, with the normal vector n1 = (0, 0, 1)
The target plane: ax + by + cz + d = 0 with the normal vector n2 = (a, b, c)
Point P: (e, f, 0) which obviously lies in the xy-plane
Now, we want to travel from point P in the direction of the normal of the target plane (because this will give us the closest point on the target plane). Hence we form an equation for the line which starts at point P, and which is parallel to the normal vector of the target plane.
Line L: (x,y,z) = (e,f,0) + t(a,b,c) = (e+ta, f+tb, tc) , where t is a real valued parameter
Next, we want to find a point on the line L which also lies on the target plane. Hence, we plug the line equation into the equation for the target plane and receive:
a(e+ta) + b(f+tb) + c * tc + d= 0
ae + bf + d + t(a2 + b2 + c 2) = 0
t = - (ae + bf + d) / (a2 + b2 + c 2)
hence the projected point will be:
Pprojected = (e + ka, f + kb, kc), where k = - (ae + bf + d) / (a2 + b2 + c 2)
With all the variables in the solution about, it might be a bit hard to grasp if you are new to the area. But really, it is rather simple. The things you have to learn are:
The standard equation for a plane: ax + by + cz + d = 0
How to extract the normal vector from the equation of the plane
What the normal vector is (a vector which is perpendicular to all position vectors in the plane)
The parametric representation of a line: (x, y, z) = u + t*v* , where u is a point which lies on the line, t is a real valued parameter and v is a vector parallel to the line.
The understanding that the closest path between a point and a plane will be parallel to the normal of the plane
If you grasped the above concepts, computing the projection is simple:
Compute the normal vector of the target plane. Starting from the point that you want to project, travel parallel to the computed normal vector until you reach the plane.