激光在任意形状内弹跳

Laser bouncing inside arbitrary shape

这个问题我已经在脑海里琢磨了一段时间了。给定一个任意封闭的二维多边形,它定义明确,由直线和曲线组成(为了保持一致性,可能是贝塞尔曲线)。给定多边形内的起点和方向。您将如何有效地计算从该起点开始并指向该方向的激光如何在形状内部反弹?

具体来说,假设您有一个定义形状的数组。

这是一个示例数组:

[
Line from (0, 0) to (1, 2),
Line from (1, 2) to (3, 2),
Bezier curve from (3, 2) to (3, 0) passing through (4, 1),
Line from (3, 0) to (0,0)
]

我们可以说激光起源于 (1, 0.5) 并直接向 NE 传播。

这是一张图片:

挑战在于构建一种算法,该算法将遵循直线的路径,计算激光反射时反射的交点列表和正常值。我想找到一个通用算法来解决这个给定的任何输入数组。如果您有解决方案或只是一个有用的 tip/place 开始寻找,我们将不胜感激。

要计算反射,值得用基点P0和单位方向向量dir定义射线。

当光线以线段或贝塞尔曲线的形式遇到墙,墙的法线单位为n,你有新的基点为反射点,可以计算新的方向向量:

dot = dir.x * n.x + dir.y * n.y 
//after reflection
newdir.x = dir.x - 2 * dot * n.x
newdir.y = dir.y - 2 * dot * n.y

您还需要计算射线和线段的交点(点 A-B)求解未知参数 uv 的简单方程组并检查 u为正数,v 在范围 0..1

p0.x + dir.x * u = a.x + v * (b.x - a.x)
p0.y + dir.y * u = a.y + v * (b.y - a.y)

与贝塞尔曲线相交(更复杂,但方法相同)

正常 A-B 段是

n.x = (a.y-b.y) / len(AB)
n.y = (b.x-a.x) / len(AB)

贝塞尔曲线的法线可以使用与上述类似 y/x 交换的交点中的切线(曲线方程的导数)来计算

首先,您创建一条射线,其起点为 p,方向向量为 d

然后找到任意形状的射线的交点。

然后求交点处形状的法向量

然后用反射方程vector form

求出反射方向r

r = d - 2(d · n) n

其中 n 是表面法线,d 是光线方向。

这里最具挑战性的部分是为任意形状找到射线的(最近的)交点。

I have a subroutine on GitHib 多边形进行相交检查。

您将多边形分割成线段,并检查每个线段。如果可能,选择距离最近的正距离段。如果段在光线后面,则不应命中。

这是一个二维 ray-casting 问题。

给定起点和方向,计算射线的隐式方程 s(x, y):= ax + by + c = 0,这是一条直线,您想要找到与形状轮廓的第一个交点。

在多边形的情况下,如果边的端点在射线的任一侧,则边只能与线相交,其特征是符号变化s。当有相交时,可以计算射线的参数方程的参数tP = P0 + t.D。然后第一个交点由最小的正数t给出(只要你在一个封闭的形状里面,就会有一个)。

在三次贝塞尔弧的情况下,必须修改上述符号规则,因为贝塞尔弧可以两次穿过一条线并且其端点具有相同的符号。一种可能的解决方法是将贝塞尔曲线分解为单调弧,如下:

  • 旋转线条和控制点,使线条变为水平;

  • 仅计算纵坐标的三次多项式Y(u)u是贝塞尔曲线的参数);

  • 求解二次方程Y'(u)=0Y的极值。这会为您提供范围 [0,1];

    u02
  • 用对应的点分割贝塞尔曲线,保证最多一次过线的弧线,恢复符号规则

[此过程找到与射线平行的贝塞尔曲线的切线。]

现在,对于每一个改变符号的立方弧,您都无法避免求解立方,例如使用 Cardano 公式。然后再一次,保持与最小正值的交集 t.

找到第一个交点后,计算交点处轮廓的法线方向。那么你就有了新的起点和方向,继续这个轨迹。

更新: 我弄乱了一些参数。惯例是:t 沿射线,u 沿贝塞尔曲线。

重要的是要认识到符号规则在程序中带来了安全性。为了正确起见,每次检测到符号变化时,都必须准确报告一个交叉路口。在完全等于零的情况下,您可以假设为正(前提是您始终假设为正)。