给定弧外的一点,如何找到延伸到该点的弧上的点?
Given a point outside of an arc, how can one find the point on the arc which extends to that point?
给定弧外的一点,如何找到延伸到该点的弧上的点?
例如圆(R)的半径为10cm,圆心为[0,0]。
直线 (8) 的原点 (o) 位于 [-3, 10]
我们怎样才能找到点 (p) (p8) 那个点的切线是否继续到直线的原点?
暴力解决方案是不可接受的。
设点坐标为px, py
,圆心为(0,0)
(如果不是-从所有坐标中减去圆心cx, cy
以简化方程,最后将它们加回去) .
你可以为未知数写两个方程x,y
。圆方程和垂直度一-切线垂直于半径矢量,它们的点积为零。
(x - px) * x + (y - py) * y = 0
x^2 + y^2 = r^2
x^2 - px * x + y^2 - py * y = 0
r^2 - px * x = py * y
y = (r^2 - px * x) / py
y^2 = r^4 / py ^2 - x * 2 * r^2 * px / py^2 + x^2 * px^2 / py^2
x^2 * (1 + px^2 / py^2) - x * 2 * r^2 * px / py^2 + (r^4 / py^2 - r^2) = 0
x^2 * (py^2 + px^2) - x * 2 * r^2 * px + (r^4 - r^2 * py^2) = 0
求解x
的最后一个二次方程,然后计算y
。
Delphi函数供参考(注:py=0的情况另行处理)
function GetTangentPts(px, py, r: Double): TArray<Double>;
var
px2, py2, pxpy, r2, Dis, x, y: Double;
begin
px2 := px * px;
py2 := py * py;
r2 := r * r;
pxpy := px2 + py2;
Dis := pxpy - r2;
if Dis < 0 then //point is inside
Exit(nil)
else if Dis = 0 then begin //point is at circumference
SetLength(Result, 2);
if py = 0 then begin
x := px;
y := 0;
end else begin
x := px * r2 / pxpy;
y := (r2 - px * x) / py;
end;
Result[0] := x;
Result[1] := y;
end else begin //general case, two tangents
SetLength(Result, 4);
if py = 0 then begin
y := - r * Sqrt(Dis) / px;
x := px / Abs(px) * r * Sqrt(1 - Dis/px2);
Result[0] := x;
Result[1] := y;
y := r * Sqrt(Dis) / px;
Result[2] := x;
Result[3] := y;
end else begin
x := (px * r2 - r * Sqrt(py2 * Dis)) / pxpy;
y := (r2 - px * x) / py;
Result[0] := x;
Result[1] := y;
x := (px * r2 + r * Sqrt(py2 * Dis)) / pxpy;
y := (r2 - px * x) / py;
Result[2] := x;
Result[3] := y;
end;
end;
end;
一些结果:
10.00 10.00 10.00 //two perpendicular tangents
0.00
10.00
10.00
0.00
-10.00 10.00 10.00
-10.00
0.00
0.00
10.00
1.00 1.00 10.00
//inside
0.00 10.00 10.00 //horizontal tangent
0.00
10.00
10.00 0.00 10.00 //vertical tangent
10.00
0.00
-14.14 0.00 10.00 //two tangents from OX-axis
-7.07
7.07
-7.07
-7.07
WLOG 圆以原点为圆心。我们表示圆上的一点,设(u, v)
,与到圆心和目标点(x, y)
:
的直线成直角
u (x - u) + v (y - v) = 0
或
u x + v y = r².
我们改写并平方得到
(r² - u²) y² = (r² - u x)²,
u
中的一个二次方程。由此,v
= √(r² - u²)
紧随其后,您就有了切点。
编辑:
第一个首选方法是非常基于@MBo方法的js版本修复了一些错误。
function tangentLines(centerX, centerY, radius, pX, pY) {
var horizontalAdjustment, verticalAdjustment, pX_Squared, pY_Squared,
pXY_Squared, radiusSquared, delta, x, y, result, onSameY, temp
// Center the circle at [0,0] to simplify things
onSameY = pY === centerY
horizontalAdjustment = centerX
verticalAdjustment = centerY
pX -= horizontalAdjustment
pY -= verticalAdjustment
centerX = centerY = 0
// If pY is on the same vertical as centerY then temporarily swap pX and pY
// to avoid bug caused by division of 0
if(onSameY){
temp = pY
pY = pX
pX = temp
}
pX_Squared = pX * pX
pY_Squared = pY * pY
radiusSquared = radius * radius
pXY_Squared = pX_Squared + pY_Squared
delta = pY_Squared * (pXY_Squared - radiusSquared)
// Point is inside i.e. no tangent
if (delta < 0 || pXY_Squared < radiusSquared) {
return false
}
// Point is on circumference only one tangent point
if (delta === 0) {
x = (pX * radiusSquared)
if (pXY_Squared) { x /= pXY_Squared }
y = (radiusSquared - pX * x)
if (pY) { y /= pY }
x += horizontalAdjustment
y += verticalAdjustment
return onSameY ? [y,x] : [x, y]
}
// Regular case point is outside of tangent there are 2 tangent points
x = (pX * radiusSquared - radius * Math.sqrt(delta))
if (pXY_Squared) { x /= pXY_Squared }
y = (radiusSquared - pX * x)
if (pY) { y /= pY }
x += horizontalAdjustment
y += verticalAdjustment
result = [
onSameY ? [y, x] : [x, y],
[]
]
x = (pX * radiusSquared + radius * Math.sqrt(delta))
if (pXY_Squared) { x /= pXY_Squared }
y = (radiusSquared - pX * x)
if (pY) { y /= pY }
x += horizontalAdjustment
y += verticalAdjustment
result[1] = onSameY ? [y, x] : [x, y]
return result
}
new Vue({
el: '#app',
template: `
<div>
<div>
centerX: <input v-model="centerX" @input="intersectionPoints">
centerY: <input v-model="centerY" @input="intersectionPoints">
<div>radius: <input v-model="radius" @input="intersectionPoints"></div>
</div>
<div>
pointX: <input v-model="pointX" @input="intersectionPoints">
pointY: <input v-model="pointY" @input="intersectionPoints">
</div>
<div v-if=result>
<div>Insect points: {{result}}</div>
</div>
<div v-if=!result>No intersections :-(</div>
</div>
`,
data: function() {
return {
centerX: 200,
centerY: 200,
radius: 100,
pointX: 160,
pointY: 100,
result: null
};
},
methods: {
intersectionPoints() {
this.result = tangentLines(
+this.centerX,
+this.centerY,
+this.radius,
+this.pointX,
+this.pointY
);
}
},
mounted: function() {
this.intersectionPoints();
}
});
// Without Vue just use something like
// tangentLines(200, 200, 100, 160, 100);
div {
margin: .5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
这是我想出的原始代码。
- 以
o
和 p
之间距离的一半为半径画一个圆,以直线 op
. 的中点为原点
- 计算原圆与新圆的交点
// Function to find tangent intersection points to a point outside of the circle
// Credits
// https://math.stackexchange.com/questions/3541795/given-a-point-outside-of-an-arc-how-can-one-find-the-point-on-the-arc-which-ext/3541928#3541928
//
/**
*
*
* @param {number} centerX1 Center of circle 1 X
* @param {number} centerY1 Center of circle 1 Y
* @param {number} radius1 Radius of circle 1
* @param {number} centerX2 Center of circle 2 X
* @param {number} centerY2 Center of circle 2 Y
* @param {number} radius2 Radius of circle 2
* @returns {object | boolean} The to intersect points { point1: [x1, y1], point2: [x2, y2] } or false
* @credit Math based on https://www.analyzemath.com/CircleEq/circle_intersection_calcu.html
*/
var circleIntersections = function(
centerX1,
centerY1,
radius1,
centerX2,
centerY2,
radius2
) {
var a, b, c, A, B, C, delta, x1, x2, y1, y2;
a = -(centerY1 - centerY2) / (centerX1 - centerX2);
b = 2 * (centerX1 - centerX2);
c =
(-radius1 * radius1 +
radius2 * radius2 +
centerX1 * centerX1 -
centerX2 * centerX2 +
centerY1 * centerY1 -
centerY2 * centerY2) /
b;
A = a * a + 1;
B = 2 * a * c - 2 * centerX1 * a - 2 * centerY1;
C =
c * c +
centerX1 * centerX1 -
2 * centerX1 * c +
centerY1 * centerY1 -
radius1 * radius1;
delta = B * B - 4 * A * C;
if (delta < 0) {
return false;
}
y1 = (-B + Math.sqrt(delta)) / (2 * A);
x1 = a * y1 + c;
y2 = (-B - Math.sqrt(delta)) / (2 * A);
x2 = a * y2 + c;
return {
point1: [x1, y1],
point2: [x2, y2]
};
};
/**
*
*
* @param {number} centerX Center of circle X
* @param {number} centerY Center of circle Y
* @param {number} radius Radius of circle
* @param {number} pointX Point to tangent to X
* @param {number} pointY Point to tangent to Y
* @returns {object | boolean} The to intersect points { point1: [x1, y1], point2: [x2, y2] } or false
*/
var tangentLines = function(centerX, centerY, radius, pointX, pointY) {
var centerX2, centerY2, radius2, dX, dY;
centerX2 = centerX - (centerX - pointX) / 2;
centerY2 = centerY - (centerY - pointY) / 2;
dX = centerX2 - centerX;
dY = centerY2 - centerY;
radius2 = Math.sqrt(dX * dX + dY * dY);
return circleIntersections(
centerX,
centerY,
radius,
centerX2,
centerY2,
radius2
);
};
new Vue({
el: '#app',
template: `
<div>
<div>
centerX: <input v-model="centerX" @input="intersectionPoints">
centerY: <input v-model="centerY" @input="intersectionPoints">
<div>radius: <input v-model="radius" @input="intersectionPoints"></div>
</div>
<div>
pointX: <input v-model="pointX" @input="intersectionPoints">
pointY: <input v-model="pointY" @input="intersectionPoints">
</div>
<div v-if=result>
<div>point1: {{result.point1}}</div>
<div>point2: {{result.point2}}</div>
</div>
<div v-if=!result>No intersections :-(</div>
</div>
`,
data: function() {
return {
centerX: 200,
centerY: 200,
radius: 100,
pointX: 160,
pointY: 100,
result: null
};
},
methods: {
intersectionPoints() {
this.result = tangentLines(
this.centerX,
this.centerY,
this.radius,
this.pointX,
this.pointY
);
}
},
mounted: function() {
this.intersectionPoints();
}
});
// Without Vue just use something like
// tangentLines(200, 200, 100, 160, 100);
div {
margin:5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id=app></div>
给定弧外的一点,如何找到延伸到该点的弧上的点?
例如圆(R)的半径为10cm,圆心为[0,0]。
直线 (8) 的原点 (o) 位于 [-3, 10]
我们怎样才能找到点 (p) (p8) 那个点的切线是否继续到直线的原点?
暴力解决方案是不可接受的。
设点坐标为px, py
,圆心为(0,0)
(如果不是-从所有坐标中减去圆心cx, cy
以简化方程,最后将它们加回去) .
你可以为未知数写两个方程x,y
。圆方程和垂直度一-切线垂直于半径矢量,它们的点积为零。
(x - px) * x + (y - py) * y = 0
x^2 + y^2 = r^2
x^2 - px * x + y^2 - py * y = 0
r^2 - px * x = py * y
y = (r^2 - px * x) / py
y^2 = r^4 / py ^2 - x * 2 * r^2 * px / py^2 + x^2 * px^2 / py^2
x^2 * (1 + px^2 / py^2) - x * 2 * r^2 * px / py^2 + (r^4 / py^2 - r^2) = 0
x^2 * (py^2 + px^2) - x * 2 * r^2 * px + (r^4 - r^2 * py^2) = 0
求解x
的最后一个二次方程,然后计算y
。
Delphi函数供参考(注:py=0的情况另行处理)
function GetTangentPts(px, py, r: Double): TArray<Double>;
var
px2, py2, pxpy, r2, Dis, x, y: Double;
begin
px2 := px * px;
py2 := py * py;
r2 := r * r;
pxpy := px2 + py2;
Dis := pxpy - r2;
if Dis < 0 then //point is inside
Exit(nil)
else if Dis = 0 then begin //point is at circumference
SetLength(Result, 2);
if py = 0 then begin
x := px;
y := 0;
end else begin
x := px * r2 / pxpy;
y := (r2 - px * x) / py;
end;
Result[0] := x;
Result[1] := y;
end else begin //general case, two tangents
SetLength(Result, 4);
if py = 0 then begin
y := - r * Sqrt(Dis) / px;
x := px / Abs(px) * r * Sqrt(1 - Dis/px2);
Result[0] := x;
Result[1] := y;
y := r * Sqrt(Dis) / px;
Result[2] := x;
Result[3] := y;
end else begin
x := (px * r2 - r * Sqrt(py2 * Dis)) / pxpy;
y := (r2 - px * x) / py;
Result[0] := x;
Result[1] := y;
x := (px * r2 + r * Sqrt(py2 * Dis)) / pxpy;
y := (r2 - px * x) / py;
Result[2] := x;
Result[3] := y;
end;
end;
end;
一些结果:
10.00 10.00 10.00 //two perpendicular tangents
0.00
10.00
10.00
0.00
-10.00 10.00 10.00
-10.00
0.00
0.00
10.00
1.00 1.00 10.00
//inside
0.00 10.00 10.00 //horizontal tangent
0.00
10.00
10.00 0.00 10.00 //vertical tangent
10.00
0.00
-14.14 0.00 10.00 //two tangents from OX-axis
-7.07
7.07
-7.07
-7.07
WLOG 圆以原点为圆心。我们表示圆上的一点,设(u, v)
,与到圆心和目标点(x, y)
:
u (x - u) + v (y - v) = 0
或
u x + v y = r².
我们改写并平方得到
(r² - u²) y² = (r² - u x)²,
u
中的一个二次方程。由此,v
= √(r² - u²)
紧随其后,您就有了切点。
编辑: 第一个首选方法是非常基于@MBo方法的js版本修复了一些错误。
function tangentLines(centerX, centerY, radius, pX, pY) {
var horizontalAdjustment, verticalAdjustment, pX_Squared, pY_Squared,
pXY_Squared, radiusSquared, delta, x, y, result, onSameY, temp
// Center the circle at [0,0] to simplify things
onSameY = pY === centerY
horizontalAdjustment = centerX
verticalAdjustment = centerY
pX -= horizontalAdjustment
pY -= verticalAdjustment
centerX = centerY = 0
// If pY is on the same vertical as centerY then temporarily swap pX and pY
// to avoid bug caused by division of 0
if(onSameY){
temp = pY
pY = pX
pX = temp
}
pX_Squared = pX * pX
pY_Squared = pY * pY
radiusSquared = radius * radius
pXY_Squared = pX_Squared + pY_Squared
delta = pY_Squared * (pXY_Squared - radiusSquared)
// Point is inside i.e. no tangent
if (delta < 0 || pXY_Squared < radiusSquared) {
return false
}
// Point is on circumference only one tangent point
if (delta === 0) {
x = (pX * radiusSquared)
if (pXY_Squared) { x /= pXY_Squared }
y = (radiusSquared - pX * x)
if (pY) { y /= pY }
x += horizontalAdjustment
y += verticalAdjustment
return onSameY ? [y,x] : [x, y]
}
// Regular case point is outside of tangent there are 2 tangent points
x = (pX * radiusSquared - radius * Math.sqrt(delta))
if (pXY_Squared) { x /= pXY_Squared }
y = (radiusSquared - pX * x)
if (pY) { y /= pY }
x += horizontalAdjustment
y += verticalAdjustment
result = [
onSameY ? [y, x] : [x, y],
[]
]
x = (pX * radiusSquared + radius * Math.sqrt(delta))
if (pXY_Squared) { x /= pXY_Squared }
y = (radiusSquared - pX * x)
if (pY) { y /= pY }
x += horizontalAdjustment
y += verticalAdjustment
result[1] = onSameY ? [y, x] : [x, y]
return result
}
new Vue({
el: '#app',
template: `
<div>
<div>
centerX: <input v-model="centerX" @input="intersectionPoints">
centerY: <input v-model="centerY" @input="intersectionPoints">
<div>radius: <input v-model="radius" @input="intersectionPoints"></div>
</div>
<div>
pointX: <input v-model="pointX" @input="intersectionPoints">
pointY: <input v-model="pointY" @input="intersectionPoints">
</div>
<div v-if=result>
<div>Insect points: {{result}}</div>
</div>
<div v-if=!result>No intersections :-(</div>
</div>
`,
data: function() {
return {
centerX: 200,
centerY: 200,
radius: 100,
pointX: 160,
pointY: 100,
result: null
};
},
methods: {
intersectionPoints() {
this.result = tangentLines(
+this.centerX,
+this.centerY,
+this.radius,
+this.pointX,
+this.pointY
);
}
},
mounted: function() {
this.intersectionPoints();
}
});
// Without Vue just use something like
// tangentLines(200, 200, 100, 160, 100);
div {
margin: .5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
这是我想出的原始代码。
- 以
o
和p
之间距离的一半为半径画一个圆,以直线op
. 的中点为原点
- 计算原圆与新圆的交点
// Function to find tangent intersection points to a point outside of the circle
// Credits
// https://math.stackexchange.com/questions/3541795/given-a-point-outside-of-an-arc-how-can-one-find-the-point-on-the-arc-which-ext/3541928#3541928
//
/**
*
*
* @param {number} centerX1 Center of circle 1 X
* @param {number} centerY1 Center of circle 1 Y
* @param {number} radius1 Radius of circle 1
* @param {number} centerX2 Center of circle 2 X
* @param {number} centerY2 Center of circle 2 Y
* @param {number} radius2 Radius of circle 2
* @returns {object | boolean} The to intersect points { point1: [x1, y1], point2: [x2, y2] } or false
* @credit Math based on https://www.analyzemath.com/CircleEq/circle_intersection_calcu.html
*/
var circleIntersections = function(
centerX1,
centerY1,
radius1,
centerX2,
centerY2,
radius2
) {
var a, b, c, A, B, C, delta, x1, x2, y1, y2;
a = -(centerY1 - centerY2) / (centerX1 - centerX2);
b = 2 * (centerX1 - centerX2);
c =
(-radius1 * radius1 +
radius2 * radius2 +
centerX1 * centerX1 -
centerX2 * centerX2 +
centerY1 * centerY1 -
centerY2 * centerY2) /
b;
A = a * a + 1;
B = 2 * a * c - 2 * centerX1 * a - 2 * centerY1;
C =
c * c +
centerX1 * centerX1 -
2 * centerX1 * c +
centerY1 * centerY1 -
radius1 * radius1;
delta = B * B - 4 * A * C;
if (delta < 0) {
return false;
}
y1 = (-B + Math.sqrt(delta)) / (2 * A);
x1 = a * y1 + c;
y2 = (-B - Math.sqrt(delta)) / (2 * A);
x2 = a * y2 + c;
return {
point1: [x1, y1],
point2: [x2, y2]
};
};
/**
*
*
* @param {number} centerX Center of circle X
* @param {number} centerY Center of circle Y
* @param {number} radius Radius of circle
* @param {number} pointX Point to tangent to X
* @param {number} pointY Point to tangent to Y
* @returns {object | boolean} The to intersect points { point1: [x1, y1], point2: [x2, y2] } or false
*/
var tangentLines = function(centerX, centerY, radius, pointX, pointY) {
var centerX2, centerY2, radius2, dX, dY;
centerX2 = centerX - (centerX - pointX) / 2;
centerY2 = centerY - (centerY - pointY) / 2;
dX = centerX2 - centerX;
dY = centerY2 - centerY;
radius2 = Math.sqrt(dX * dX + dY * dY);
return circleIntersections(
centerX,
centerY,
radius,
centerX2,
centerY2,
radius2
);
};
new Vue({
el: '#app',
template: `
<div>
<div>
centerX: <input v-model="centerX" @input="intersectionPoints">
centerY: <input v-model="centerY" @input="intersectionPoints">
<div>radius: <input v-model="radius" @input="intersectionPoints"></div>
</div>
<div>
pointX: <input v-model="pointX" @input="intersectionPoints">
pointY: <input v-model="pointY" @input="intersectionPoints">
</div>
<div v-if=result>
<div>point1: {{result.point1}}</div>
<div>point2: {{result.point2}}</div>
</div>
<div v-if=!result>No intersections :-(</div>
</div>
`,
data: function() {
return {
centerX: 200,
centerY: 200,
radius: 100,
pointX: 160,
pointY: 100,
result: null
};
},
methods: {
intersectionPoints() {
this.result = tangentLines(
this.centerX,
this.centerY,
this.radius,
this.pointX,
this.pointY
);
}
},
mounted: function() {
this.intersectionPoints();
}
});
// Without Vue just use something like
// tangentLines(200, 200, 100, 160, 100);
div {
margin:5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id=app></div>