给定一条以第一个端点P(x1,y1)为起点的线段,另一个端点未知,与位于原点、半径为R的圆在唯一一点(切点)T(x2,y2)相交。有人知道如何获得点T吗?提前致谢!
__...------__ T(x2, y2)
_.-'' -(+)
,-' |----
,' | ----
,' | ' ----
/ | ` ----
/ | `. ----
/ | \ ----
| | | ----
| | | ----
| | | ----
| (+)---------------------------------------------(+) P (x1,y1)
| .'
| O |
| .'
\ /
\ ,'
` /
'. ,'
'-. _,'
'-._ _,(+) T'(x3, y3)
'`--......---'
对于TO
,有两个可能的方向,因为点T'也是一个有效的切点,所以你将会得到两个相等的三角形。
OP
是 OPT
的外接圆的直径。 - Ingo Bürkdmckee的回答包含了你需要的所有内容,但如果你想查看一些代码,请查看使用Javascript和HTML canvas实现的以下示例。
完整示例: http://jsfiddle.net/zxqCw/1/
// find tangents
dx = cx - px;
dy = cy - py;
dd = Math.sqrt(dx * dx + dy * dy);
a = Math.asin(radius / dd);
b = Math.atan2(dy, dx);
t = b - a
ta = { x:radius * Math.sin(t), y:radius * -Math.cos(t) };
t = b + a
tb = { x:radius * -Math.sin(t), y:radius * Math.cos(t) };
设 R
为圆的半径,D
为从外部点到圆心的距离,且满足 D > R
。
切线与连接外部点和圆心的线的夹角为 \alpha
,其中
\alpha = arcsin(R/D)
连接外部点 (P
) 和中心 (C
) 的直线与水平线的夹角为
\beta = arctan((C_y - P_y)/(C_x - P_x))
这会给你切线与水平方向的夹角
\theta = \beta +/- \alpha
请问是否需要语言指定?如果需要,可以告诉我翻译成何种语言呢?L = sqrt(D^2 - R^2)
这就是你所需要的全部。
arcsin
。你应该使用 arccos
来求连接线 (CP) 和半径 (CT) 之间的夹角。 - dmckee --- ex-moderator kittenimbrizi的答案假设圆心为(0,0)。
以下是Objective C中正确的答案:
- (NSArray *)pointsTangentToCircleWithCenter:(CGPoint)centerPoint
radius:(CGFloat)radius
outerPoint:(CGPoint)outerPoint {
float dx = centerPoint.x - outerPoint.x;
float dy = centerPoint.y - outerPoint.y;
float dd = sqrt(dx*dx + dy*dy);
float a = asinf(radius / dd);
float b = atan2f(dy, dx);
float t1 = b - a;
CGPoint tangentPoint1 = CGPointMake(centerPoint.x + radius*sinf(t1),
centerPoint.y + radius*-cosf(t1));
float t2 = b + a;
CGPoint tangentPoint2 = CGPointMake(centerPoint.x + radius*-sinf(t2),
centerPoint.y + radius*cosf(t2));
NSArray *points = @[
[NSValue valueWithCGPoint:tangentPoint1],
[NSValue valueWithCGPoint:tangentPoint2]
];
return points;
}
如果你将矢量 DO 旋转角度 alpha (角度可以通过 asin(len(OX) / len(DO)) 找到,这是半径与斜边之比的反正弦函数),就可以找到向量 DX 的方向
你可以轻松地按如下方式找到向量 DX 的长度:sqrt(len(DO)*len(DO) - len(OX)*len(OX))
已知向量 DX 的方向和长度,你可以找到点 X的值,一种方法是将 DX 归一化并将其乘以它的长度。
auto dist = D.Distance(O); auto side = sqrt(dist*dist - rad*rad) auto line = Vector2D(D, O); line.Rotate(asin(rad / dist)); //get the direction line.Normalize(); //set length to 1 line*=side; //we have the direction, now get length Point2D X = D + line;
P.S. 注意,还有第二条正切线,它是通过将 DO 逆时针旋转 alpha 角度找到的。
我不确定这是否是一份作业,但我喜欢直角三角形的定义。即便如此,解决问题仍需要一些代数运算。
另一种看起来可行的方法是将问题简单地定义为两个未知数的两个方程的解。也就是说,以坐标为(0,0),半径为R的圆的方程为
x^2 + y^2 = R^2
(y - yt) = S*(x - xt)
我通常使用Maple软件来解决这类问题,它甚至可以从这些方程式生成C代码。
Here's the output:
t1 = v_x * v_x;
t2 = t1 * t1;
t3 = v_y * v_y;
t6 = sqrt(t1 * t3 - t1 + t2);
t7 = v_y + t6;
t9 = 0.1e1 / (t1 + t3);
t13 = 0.1e1 / v_x;
x1 = -(t7 * t9 * v_y - 0.1e1) * t13;
y1 = t7 * t9;
t16 = (-v_y + t6) * t9;
x2 = -(-t16 * v_y - 0.1e1) * t13;
y2 = -t16;
public static double[] GetTangentPoints(
double px,
double py,
double cx,
double cy,
double radius)
{
var points = new double[4];
var dx = cx - px;
var dy = cy - py;
if (dx == 0 && dy == 0)
return null; // no solution
// PC is distance between P and C, pc2 is PC^2
var pc2 = dx * dx + dy * dy;
var pc = Math.Sqrt(pc2);
if (pc < radius)
return null; // no solution
// R is radius of circle centered in P, r2 is R^2
var r2 = pc2 - radius * radius;
// d is the P => X0 distance (demonstration is here https://mathworld.wolfram.com/Circle-CircleIntersection.html where PC is named 'd' in there)
var d = r2 / pc;
// h is the X0 => X1 (and X0 => X2) distance
var h = Math.Sqrt(r2 - d * d);
// first tangent point
points[0] = px + (dx * d - dy * h) / pc;
points[1] = py + (dy * d + dx * h) / pc;
// second tangent point
points[2] = px + (dx * d + dy * h) / pc;
points[3] = py + (dy * d - dx * h) / pc;
return points;
}
这是我的C#答案。只有7行代码。你必须观看“Youtube A Miraculous Proof (Ptolemy's Theorem) - Numberphile”才能弄清楚它的工作原理。该代码已经测试并产生了正确的答案。抱歉插入了换行符。我只是从Visual Studio复制到这里。
static void FindTangentPoints(float r, PointF p, out PointF tangent1, out PointF tangent2)
{
float incidentLength = MathF.Sqrt(p.X * p.X + p.Y * p.Y); //the distance from the origin to the point outside the circle
float reflectedLength = (r * r) / incidentLength; //see Youtube A Miraculous Proof (Ptolemy's Theorem) - Numberphile
//take particular note when Prof Stankova writes OA1 * OA = R*R
PointF p2 = new PointF(p.X * reflectedLength / incidentLength, p.Y * reflectedLength / incidentLength); //the inversion in the plane of pointOutsideTheCircle
float pRadians = MathF.Atan2(p.Y, p.X); //the same as the angle to point p2
float angleP2OT = MathF.Acos(MathF.Sqrt(p2.X * p2.X + p2.Y * p2.Y) / r); //the angle between the tangent point and p
tangent1 = new PointF(r * MathF.Cos(pRadians + angleP2OT), r * MathF.Sin(pRadians + angleP2OT));
tangent2 = new PointF(r * MathF.Cos(pRadians - angleP2OT), r * MathF.Sin(pRadians - angleP2OT));
}