我有一个简单的2D光线投射程序,当障碍物数量增加时,它会变得非常缓慢
该例行程序由以下部分组成:
2for
个循环(外循环在每条光线/方向上迭代,然后内循环在每条线障碍物上迭代)
多个if
语句(检查一个值是否大于另一个值或数组是否为空)
问题:如何使用Numpy将所有这些操作压缩成一个向量化指令块
更具体地说,我面临两个问题:
我已设法对内部循环(光线和每个障碍物之间的交点)进行矢量化,但无法同时对所有光线运行此操作
我发现处理if
语句的唯一解决方法是使用掩码数组。有些事情告诉我,在这种情况下,处理这些陈述的方式并不恰当(它看起来笨拙、笨重、不和谐)
原始代码:
from math import radians, cos, sin
import matplotlib.pyplot as plt
import numpy as np
N = 10 # dimensions of canvas (NxN)
sides = np.array([[0, N, 0, 0], [0, N, N, N], [0, 0, 0, N], [N, N, 0, N]])
edges = np.random.rand(5, 4) * N # coordinates of 5 random segments (x1, x2, y1, y2)
edges = np.concatenate((edges, sides))
center = np.array([N/2, N/2]) # coordinates of center point
directions = np.array([(cos(radians(a)), sin(radians(a))) for a in range(0, 360, 10)]) # vectors pointing in all directions
intersections = []
# for each direction
for d in directions:
min_dist = float('inf')
# for each edge
for e in edges:
p1x, p1y = e[0], e[2]
p2x, p2y = e[1], e[3]
p3x, p3y = center
p4x, p4y = center + d
# find intersection point
den = (p1x - p2x) * (p3y - p4y) - (p1y - p2y) * (p3x - p4x)
if den:
t = ((p1x - p3x) * (p3y - p4y) - (p1y - p3y) * (p3x - p4x)) / den
u = -((p1x - p2x) * (p1y - p3y) - (p1y - p2y) * (p1x - p3x)) / den
# if any:
if t > 0 and t < 1 and u > 0:
sx = p1x + t * (p2x - p1x)
sy = p1y + t * (p2y - p1y)
isec = np.array([sx, sy])
dist = np.linalg.norm(isec-center)
# make sure to select the nearest one (from center)
if dist < min_dist:
min_dist = dist
nearest = isec
# store nearest interesection point for each ray
intersections.append(nearest)
# Render
plt.axis('off')
for x, y in zip(edges[:,:2], edges[:,2:]):
plt.plot(x, y)
for isec in np.array(intersections):
plt.plot((center[0], isec[0]), (center[1], isec[1]), '--', color="#aaaaaa", linewidth=.8)
矢量化版本(尝试):
from math import radians, cos, sin
import matplotlib.pyplot as plt
from scipy import spatial
import numpy as np
N = 10 # dimensions of canvas (NxN)
sides = np.array([[0, N, 0, 0], [0, N, N, N], [0, 0, 0, N], [N, N, 0, N]])
edges = np.random.rand(5, 4) * N # coordinates of 5 random segments (x1, x2, y1, y2)
edges = np.concatenate((edges, sides))
center = np.array([N/2, N/2]) # coordinates of center point
directions = np.array([(cos(radians(a)), sin(radians(a))) for a in range(0, 360, 10)]) # vectors pointing in all directions
intersections = []
# Render edges
plt.axis('off')
for x, y in zip(edges[:,:2], edges[:,2:]):
plt.plot(x, y)
# for each direction
for d in directions:
p1x, p1y = edges[:,0], edges[:,2]
p2x, p2y = edges[:,1], edges[:,3]
p3x, p3y = center
p4x, p4y = center + d
# denominator
den = (p1x - p2x) * (p3y - p4y) - (p1y - p2y) * (p3x - p4x)
# first 'if' statement -> if den > 0
mask = den > 0
den = den[mask]
p1x = p1x[mask]
p1y = p1y[mask]
p2x = p2x[mask]
p2y = p2y[mask]
t = ((p1x - p3x) * (p3y - p4y) - (p1y - p3y) * (p3x - p4x)) / den
u = -((p1x - p2x) * (p1y - p3y) - (p1y - p2y) * (p1x - p3x)) / den
# second 'if' statement -> if (t>0) & (t<1) & (u>0)
mask2 = (t > 0) & (t < 1) & (u > 0)
t = t[mask2]
p1x = p1x[mask2]
p1y = p1y[mask2]
p2x = p2x[mask2]
p2y = p2y[mask2]
# x, y coordinates of all intersection points in the current direction
sx = p1x + t * (p2x - p1x)
sy = p1y + t * (p2y - p1y)
pts = np.c_[sx, sy]
# if any:
if pts.size > 0:
# find nearest intersection point
tree = spatial.KDTree(pts)
nearest = pts[tree.query(center)[1]]
# Render
plt.plot((center[0], nearest[0]), (center[1], nearest[1]), '--', color="#aaaaaa", linewidth=.8)
问题的重新表述–查找线段和线光线之间的交点
设
q
和q2
为段(障碍物)的端点。为了方便起见,让我们定义一个类来表示平面中的点和向量。除了通常的操作外,向量乘法由u × v = u.x * v.y - u.y * v.x
定义现在,我发现在极坐标系中处理光线更容易:对于给定的角度
theta
(方向),目标是确定它是否与线段相交,如果是,则确定相应的半径。这里有一个函数可以找到它。请参见here了解原因和方式的解释。我尝试使用与上一个链接中相同的变量名例如
find_intersect_btw_ray_and_sgmt(Coord(1, 2), Coord(-1, 2), np.pi / 2)
应该返回2
让我们把它矢量化
这里非常有趣的是
Coord
类中定义的操作也适用于x和y是numpy数组的情况!因此,对Coord(np.array([1, 2, 0]), np.array([2, -1, 3]))
应用任何已定义的操作都等于将其元素级应用于点(1, 2), (2, -1) and (0, 3)
。因此Coord
的操作已经矢量化了。构造函数可以修改为:现在,我们希望函数
find_intersect_btw_ray_and_sgmt
能够处理参数q
和q2
包含端点序列的情况。在进行健全性检查之前,所有操作都正常工作,因为正如我们所提到的,它们已经矢量化了。正如您提到的,条件语句可以使用掩码“矢量化”。以下是我的建议:对于给定的θ值,我们可以确定相应的光线是否同时与多个线段相交
这还不是全部!多亏了python的传播,才有可能避免对θ值的迭代。例如,回想一下
np.array([1, 2, 3]) * np.array([[1], [2], [3], [4]])
生成一个大小为4×3的成对乘积矩阵。以同样的方式Coord([[5],[7]], [[5],[1]]) * Coord([2, 4, 6], [-2, 4, 0])
输出一个2×3矩阵,其中包含向量(5,5)、(7,1)和(2,-2)、(4,4)、(6,0)之间的所有成对叉积最后,可通过以下方式确定交叉点:
相关问题 更多 >
编程相关推荐