问题指出
朴素的薄厚法建立在「瞄准是正交投影」的假设上,也即相当于球员从足够远的地方瞄准,以至于上图中的线 $AB$ 与 $CH$ 足够平行,才能保证最后的击球位置确实与预期相符。
例如以欲击打 30° 球为例,那么根据上表,应切的厚度为一倍半径。
根据切球厚度的字面义,应当使母球中心瞄准目标球边缘进行击打。
倘若两球距离过近,那么如下图,半透明黑色为预计的撞击位置与初球路线,而红色为实际情况,可见两者相去甚远。
更极端地,如果母球与目标球相贴,那么(只考虑几何因素)不论击球角度如何变化,目标球都会以直接远离母球的方向被击出。
从这我们可以看出,两球间距离过近,对于薄厚法的结果出球角度有削弱作用;距离越近,削弱越多。
真实公式
如上图,圆 $A,B,C$ 仍如上所述为母球、目标球、碰撞发生时母球所在位置,两球半径均为 $r$。
母球原始球心位置到目标球球心距离为 $|AB|=dr$,即 $d$ 倍半径。
在成角透视中,谈论“偏移距离”没有意义,只有“偏移角度”有意义。
因此在这种情况下,应当以角度论切球薄厚。
过母球球心 $A$ 作目标球切线切之于 $H$,则 $AH$ 即为球员通过母球球心瞄准目标球边缘时的射线。
记 $\angle BAH=\varphi$,即切球的一倍半径标准厚度。
又记击球方向 $AC$ 与两球连线 $AB$ 之间的夹角 $\angle BAC=x\varphi$ 为实际切球厚度,相当于朝 $x$ 倍半径击球。
最后,记目标球出射角度为 $\theta=\angle ABC$。
接下来就是解三角形 $\Delta ABC$ 的事了。
我们已经知道了:
$$\begin{cases}
|AB|=dr, \\
|BC|=2r, \\
\angle ABC=\theta,
\end{cases}$$
(注意,$|AC|$ 不一定等于 $r$,只不过图例恰好接近罢了)
希望解出 $\angle BAC$。
可以先通过余弦定理得到
$$
|AC|^2
=|AB|^2+|BC|^2-2\cos\theta\cdot|AB|\cdot|BC|
=r^2(d^2+4-4d\cos\theta),
$$
再逆练余弦定理得到
$$
x\varphi=\angle BAC
=\arccos\left(\frac
{|AB|^2+|AC|^2-|BC|^2}
{2\cdot|AB|\cdot|AC|}
\right)
=\arccos\left(\frac
{d-2\cos\theta}
{\sqrt{d^2+4-4d\cos\theta}}
\right).
$$
最后,很直接地,$\varphi=\arcsin(\frac{1}{d})$。
因此得到
$$\begin{equation}
f(\theta,d):=x=\frac
{\arccos\left(\frac
{d-2\cos\theta}
{\sqrt{d^2+4-4d\cos\theta}}
\right)}
{\arcsin(\frac{1}{d})}.
\end{equation}$$
……这玩意没法化简。
下面的图表可视化地展示了上述公式在 $d$ 设为不同值情况下的样子。
其中:
- 横轴为期望出球角度 $\theta$($0\leq\theta<\pi/2$),纵轴为切球厚度 $x$(仅 $0\leq x<2$ 时有意义,图中未截断)。
- 红色虚线是根据朴素版本中的极限情况得出的曲线,即 $x=2\sin\theta$。
- 蓝、绿、黄三条线分别对应 $d=2.1,4,20$(即两球分别相距 $0.1,2,18$ 个半径远)时的期望角度—切球厚度曲线。
可以看出:
- $d$ 越大(即两球间隔越大),实际出球角度越接近朴素公式。
这是我们期望看到的结果。
- $d$ 越小,要打出同样角度的球就得切得更厚,符合我们的预期。
- 对于过小的 $d$,可以看到曲线会在定义域范围内升高过 $x=2$。
这是因为两球相距过近时,理论上无法通过直接超薄切打出角度过大的球。
- 并且,还会在 $\theta<\pi/2$ 之前落回 $x=2$ 下,但这并不代表球手可以打出“角度极大”的球。
相反,那些范围是无效的,因为它们代表了母球先“穿过”目标球,第二次与目标球相切时再发生碰撞的情况。
这是违反物理语义的。
上述观察证明了我们推得的公式确实正确。
应用
这个公式准是准,但是太复杂了,球手根本没法计算!
就算背表,也得根据两球之间间距的不同情况背好几张表,非常不现实。
因此,我希望寻求一种方法,使得球手可以通过一个简单的公式,在有需要时可以根据实际间距对朴素版本的结果加以少量“补贴”,以得到更准确的切球厚度。
解释
我要打一个 30° 的球。根据薄厚法,应该瞄准一倍半径,也就是目标球边缘打。
但是,两个球距离有点太近了,薄厚法不准确,需要做出修正。
现在的距离差不多是隔了 2 个球,也就是 6r 的宽度。
那么根据(一个简单公式),我应该朝 1+k 倍半径的位置打。
这里,能够简单近似求得这个 k 的公式,就是我想寻求的。
这就相当于对 $g(\theta,d):=f(\theta,d)-f(\theta)$ 作拟合。
这是一个看上去就没法化简的的形式,我不打算在这种尝试上浪费时间。
因此,直接扔进脚本里,对其进行 Chebyshev 逼近。
为什么不作泰勒展开取低次项?
因为泰勒展开并不能在指定次数的情况下给出误差最小的逼近。
在这个场合中,我们只关心低次拟合的误差,不关心形式上的“可扩展性”。
对 $d\in [3,6]$(间隔半颗到两颗球)之间进行线性逼近得到的结果是:
$$\begin{equation}
g(\theta,d)\approx 0.74-0.08 d.
\end{equation}$$
也就是说,如果你希望在间隔一颗球($d=4$)的情况打进一个 45° 的球,应在朴素版公式所给出的 $1.4$ 倍半径上加上 $0.74-0.08\times 4=0.42$ 倍的半径,也就是瞄准 $1.82$ 倍半径处击球。
类似地,在间隔半颗球($d=3$)时欲打 30° 的球,瞄准位置为 $1+0.74-0.08\times 3=1.5$ 倍半径。
下面是此逼近公式的误差热力图:
可以看到,此逼近公式在大多数情况下都是较为准确的,除了非常厚与非常薄的情况(这种情况要么本来就很难打准,要么根本没必要算)。