javascript - 给定一个固定的角度、宽度和矢状面,我们如何计算椭圆的水平和垂直半径?
问题描述
给定一个固定的角度、宽度和矢状面,我们如何计算椭圆的水平和垂直半径?
我想绘制一个椭圆弧,它具有给定的弧宽、高度(Sagitta)并且是椭圆的给定角度。
在下图中,黑色和黄色虚线弧线。为此,我需要知道椭圆的 2 个半径。
对于一个圆,可以很容易地计算出它的 1 个半径,给出角度、矢状面或宽度的任意 2 个值。
请参阅下面的片段。
如何调整函数以适用于椭圆?在图中,角度可以说是从 -60 到 60 度对称,形成 120 度角。这是我真正需要的情况,即弧线在垂直轴或水平轴的两侧反射自身。
在角度为 120 的情况下,从 80 开始到 200 结束,没有真正的矢状面,只有弧线的最高点和一个紧密的边界框,如果有人也有解决方案,这将更难解决成为奢侈品。
var arcCalc = function(r, w, a, s) {
// to allow for object usage
if (r instanceof Object) {
w = r.w || r.width;
a = r.a || r.angle;
s = r.s || r.sagitta;
r = r.r || r.radius;
}
w = this.toPts(w);
s = this.toPts(s);
r = this.toPts(r);
var sin, cos, twoKnown;
sin = Math.sin;
cos = Math.cos;
// if we know any two arguments then we can work out the other ones
// if not we can't
twoKnown = +!r + +!w + +!a + +!s < 3;
// At this point of time we are trying to avoid throwing errors
// so for now just throw back the garbage we received
if (!twoKnown)
return {
radius: r,
width: w,
angle: a,
sagitta: s,
r: r,
w: w,
a: a,
s: s
};
if (a) {
a *= Math.PI / 180;
}
if (!r) {
if (!s) {
r = w / (2 * sin(a / 2));
} else if (!a) {
r = (s * s + 0.5 * w * (0.5 * w)) / (2 * s);
} else {
r = s / (1 - cos(a / 2));
}
}
// at this point we know we have r
if (!w) {
if (!s) {
w = 2 * r * sin(a / 2);
} else {
w = 2 * Math.sqrt(s * (2 * r - s));
}
}
// at this point we know we have r and w
if (!a) {
if (!s) {
// We added the round because
// w / (2*r) could come to 1.00000000001
// and then NaN would be produced
a = 2 * Math.asin(this.round(w / (2 * r)));
} else {
a = 2 * Math.acos(this.round(1 - s / r));
}
}
if (!s) {
s = r - r * cos(a / 2);
}
a *= 180 / Math.PI;
return {
radius: r,
width: w,
angle: a,
sagitta: s,
r: r,
w: w,
a: a,
s: s
};
};
解决方案
给定的是角度 alpha、宽度和矢状面。圆的半径r
可以通过 alpha 计算,宽度可以通过正弦公式计算。同样,x - sagitta
从余弦公式得出。
为了找到y
,我们将绘图缩放到 x 方向,系数为y/x
。这会将椭圆转换为半径的圆y
。它将点转换[x-sagitta, width/2]
为[(x-sagitta)*y/x, width/2]
。这个变换点必须在半径为 的圆上y
。我们在 中得到一个二次方程y
:
((x-sagitta)*y/x)^2 + (width/2)^2 = y^2
.
其正解是
y = width * x * sqrt(1 / (sagitta * (2 * x - sagitta))) / 2
.
前提是2*x > sagitta
. 这减少到cos(alpha/2) > 0
或alpha < 180°
。当然,矢状面、宽度和 alpha 的极端组合会导致椭圆极度拉伸。
恢复,这给出(r 是圆的半径,x 和 y 是椭圆的轴):
r = width / 2 / sin(alpha / 2)
x = r * cos(alpha / 2) + sagitta
y = width * x / sqrt(sagitta * (2 * x - sagitta)) / 2
使用 Python 和 matplotlib 绘制所有内容可确保方程有意义:
from matplotlib import pyplot as plt
from matplotlib.patches import Ellipse
from math import sqrt, sin, cos, atan, pi
sagitta = 15
alpha = 120 * pi / 180
width = 100
r = width / 2 / sin(alpha / 2)
x = r * cos(alpha / 2) + sagitta
y = width * x / sqrt(sagitta * (2 * x - sagitta)) / 2
ax = plt.gca()
ax.plot([r * cos(alpha / 2), 0, r * cos(alpha / 2)], [- r * sin(alpha / 2), 0, r * sin(alpha / 2)], ls='-',
color='crimson')
ellipse = Ellipse((0, 0), 2 * x, 2 * y, color='purple', linewidth=1, fill=False, ls='-')
circle = Ellipse((0, 0), 2 * r, 2 * r, color='tomato', linewidth=1, fill=False, ls='-.')
lim = max(x, y) * 1.05
ax.set_xlim(-lim, lim)
ax.set_ylim(-lim, lim)
ax.axhline(0, color='silver')
ax.axvline(0, color='silver')
ax.plot([x-sagitta, x-sagitta], [width/2, -width/2], color='limegreen', ls='--')
ax.plot([x-sagitta, x], [0, 0], color='brown', ls='--')
ax.add_patch(ellipse)
ax.add_patch(circle)
ax.text(x-sagitta, width/2, ' [x-s, w/2]')
ax.set_aspect(1)
plt.show()
推荐阅读
- bootstrap-select - 带有引导选择的 FormValidation.io
- bash - 如何从 bash 脚本中的 Docker 容器中运行的脚本获取 USB 设备信息?
- javascript - 预检请求未通过访问控制检查:它没有 HTTP ok 状态
- python - 合并两个不同大小的数据框
- math - 精益是否将自己暴露为 C/C++ 或 python 库?
- tailwind-css - 顺风 + Javascript
- xpath - 如何单击 td 为空的输入?
- r - 如果在变量中找到向量,则从向量中分配名称
- python - Python(请求)如何仅解析某些 json 数据,如何使这段代码循环(不那么重要)
- ruby - 解析嵌套哈希并迭代以创建带有输出的 ruby 文件