gpt4 book ai didi

python - matplotlib.Path.contains_points : "radius" parameter defined inconsistently

转载 作者:太空狗 更新时间:2023-10-30 02:54:23 35 4
gpt4 key购买 nike

问题:

函数中的半径参数contains_point in matplotlib.path定义不一致。此函数检查指定点是在封闭路径的内部还是外部。 radius 参数用于使路径稍微变小/变大(取决于半径的符号)。这样,可以考虑/不考虑靠近路径的点。问题是,半径的符号取决于路径的方向(顺时针或逆时针)。不一致(在我看来)就在那里,因为在检查一个点是在路径内部还是外部时,路径的方向被忽略了。在严格的数学意义上说:沿路径留下的一切都包括在内。

简而言之:

如果路径逆时针方向,正半径会考虑更多点。如果路径为顺时针方向,则正半径会考虑较少的点。

示例:

在下面的示例中,检查了 3 种情况 - 分别用于顺时针和逆时针路径:

  1. 是一个包含正半径的点(靠近路径)
  2. 是一个包含负半径的点(靠近路径)
  3. 是否包含原点(在两条路径的中间)

代码:

import matplotlib.path as path
import numpy as np


verts=np.array([[-11.5, 16. ],[-11.5, -16. ],[ 11.5, -16. ],[ 11.5, 16. ],[-11.5, 16. ]])

ccwPath=path.Path(verts, closed=True)
cwPath=path.Path(verts[::-1,:], closed=True)

testPoint=[12,0]


print('contains: ','|\t', '[12,0], radius=3','|\t', '[12,0], radius=-3','|\t', '[0,0]|')

print('counterclockwise: ','|\t'
,'{0:>16s}'.format(str(ccwPath.contains_point(testPoint,radius=3) )),'|\t'
,'{0:>17s}'.format(str(ccwPath.contains_point(testPoint,radius=-3) )),'|\t'
,ccwPath.contains_point([0,0],radius=0) ,'|\t'
,'=> radius increases tolerance \t'
)

print('clockwise: ','|\t'
,'{0:>16s}'.format(str(cwPath.contains_point(testPoint,radius=3) )),'|\t'
,'{0:>17s}'.format(str(cwPath.contains_point(testPoint,radius=-3) )),'|\t'
,cwPath.contains_point([0,0],radius=0) ,'|\t'
,'=> radius decreases tolerance \t'
)

输出:

contains:          |     [12,0], radius=3 |      [12,0], radius=-3 |     [0,0]|
counterclockwise: | True | False | True | => radius increases tolerance
clockwise: | False | True | True | => radius decreases tolerance

凸路径的解决方案:

我想到的唯一想法是强制路径逆时针方向并根据此使用半径。

import matplotlib.path as path
import numpy as np


verts=np.array([[-11.5, 16. ],[-11.5, -16. ],[ 11.5, -16. ],[ 11.5, 16. ],[-11.5, 16. ]])

#comment following line out to make isCounterClockWise crash
#verts=np.array([[-11.5, 16. ],[-10,0],[-11.5, -16. ],[ 11.5, -16. ],[ 11.5, 16. ],[-11.5, 16. ]])

ccwPath=path.Path(verts, closed=True)
cwPath=path.Path(verts[::-1,:], closed=True)

testPoint=[12,0]

def isCounterClockWise(myPath):

#directions from on vertex to the other
dirs=myPath.vertices[1:]-myPath.vertices[0:-1]
#rot: array of rotations at ech edge
rot=np.cross(dirs[:-1],dirs[1:])
if len(rot[rot>0])==len(rot):
#counterclockwise
return True
elif len(rot[rot<0])==len(rot):
#clockwise
return False
else:
assert False, 'no yet implemented: This case applies if myPath is concave'

def forceCounterClockWise(myPath):
if not isCounterClockWise(myPath):
myPath.vertices=myPath.vertices[::-1]


forceCounterClockWise(cwPath)
print('contains: ','|\t', '[12,0], radius=3','|\t', '[12,0], radius=-3','|\t', '[0,0]|')

print('counterclockwise: ','|\t'
,'{0:>16s}'.format(str(ccwPath.contains_point(testPoint,radius=3) )),'|\t'
,'{0:>17s}'.format(str(ccwPath.contains_point(testPoint,radius=-3) )),'|\t'
,ccwPath.contains_point([0,0],radius=0) ,'|\t'
,'=> radius increases tolerance \t'
)

print('forced ccw: ','|\t'
,'{0:>16s}'.format(str(cwPath.contains_point(testPoint,radius=3) )),'|\t'
,'{0:>17s}'.format(str(cwPath.contains_point(testPoint,radius=-3) )),'|\t'
,cwPath.contains_point([0,0],radius=0) ,'|\t'
,'=> radius increases tolerance \t'
)

给出以下输出:

contains:          |     [12,0], radius=3 |      [12,0], radius=-3 |     [0,0]|
counterclockwise: | True | False | True | => radius increases tolerance
forced ccw: | True | False | True | => radius increases tolerance

代码注释中给出了此解决方案失败的示例(对于凹路径)。

我的问题:

  1. 有谁知道为什么会出现这种不一致?
  2. 是否有更优雅的方法来规避此问题?示例可能是:为 contains_point 使用其他库,以更智能/正确的方式使用 radius 参数或使用预定义函数查找路径的方向。

最佳答案

我认为这里唯一错误的假设是“沿路径留下的所有内容都包括在内。”。相反,contains_point 的字面意思是闭合路径是否包含一个点。

然后将半径定义为

  • 当路径逆时针方向展开时展开路径
  • 顺时针方向缩小路径

以下示例显示了这一点,其中绘制了(逆时针)路径的扩展/收缩区域中包含的点。 (红色 = 不包含点,蓝色 = 包含点)

enter image description here

import matplotlib.pyplot as plt
import matplotlib.path as path
import matplotlib.patches as patches
import numpy as np

verts=np.array([[-1, 1 ],[-1, -1 ],[ 1, -1 ],[ 1, 0 ],[ 1, 1],[-1, 1 ]])

ccwPath=path.Path(verts, closed=True)
cwPath=path.Path(verts[::-1,:], closed=True)

paths = [ccwPath, cwPath]
pathstitle = ["ccwPath", "cwPath"]
radii = [1,-1]

testPoint=(np.random.rand(400,2)-.5)*4

c = lambda p,x,r: p.contains_point(x,radius=r)

fig, axes = plt.subplots(nrows=len(paths),ncols=len(radii))

for j in range(len(paths)):
for i in range(len(radii)):
ax = axes[i,j]
r = radii[i]
patch = patches.PathPatch(paths[j], fill=False, lw=2)
ax.add_patch(patch)
col = [c(paths[j], point[0], r) for point in zip(testPoint)]
ax.scatter(testPoint[:,0], testPoint[:,1], c=col, s=8, vmin=0,vmax=1, cmap="bwr_r")
ax.set_title("{}, r={}".format(pathstitle[j],radii[i]) )

plt.tight_layout()
plt.show()

一个似乎根本没有记录的特殊性是 radius 实际上将路径扩展或缩小 radius/2.。这在上面被视为半径为 1,包括 -1.51.5 之间的点,而不是 -2< 之间的点2

关于路径的方向,可能没有一个固定的方向。如果你有 3 个点,方向可以明确地确定为顺时针、逆时针(或共线)。一旦你有了更多的点,方向的概念就不好定义了。

一个选项可能是检查路径是否“大部分是逆时针方向”。

def is_ccw(p):
v = p.vertices-p.vertices[0,:]
a = np.arctan2(v[1:,1],v[1:,0])
return (a[1:] >= a[:-1]).astype(int).mean() >= 0.5

这将允许在“大部分顺时针”路径的情况下调整半径

r = r*is_ccw(p) - r*(1-is_ccw(p))

正半径总是扩展路径,负半径总是缩小路径。

完整示例:

import matplotlib.pyplot as plt
import matplotlib.path as path
import matplotlib.patches as patches
import numpy as np

verts=np.array([[-1, 1 ],[-1, -1 ],[ 1, -1 ],[ 1, 0 ],[ 1, 1],[-1, 1 ]])

ccwPath=path.Path(verts, closed=True)
cwPath=path.Path(verts[::-1,:], closed=True)

paths = [ccwPath, cwPath]
pathstitle = ["ccwPath", "cwPath"]
radii = [1,-1]

testPoint=(np.random.rand(400,2)-.5)*4

c = lambda p,x,r: p.contains_point(x,radius=r)

def is_ccw(p):
v = p.vertices-p.vertices[0,:]
a = np.arctan2(v[1:,1],v[1:,0])
return (a[1:] >= a[:-1]).astype(int).mean() >= 0.5

fig, axes = plt.subplots(nrows=len(radii),ncols=len(paths))

for j in range(len(paths)):
for i in range(len(radii)):
ax = axes[i,j]
r = radii[i]
isccw = is_ccw(paths[j])
r = r*isccw - r*(1-isccw)
patch = patches.PathPatch(paths[j], fill=False, lw=2)
ax.add_patch(patch)
col = [c(paths[j], point[0], r) for point in zip(testPoint)]
ax.scatter(testPoint[:,0], testPoint[:,1], c=col, s=8, vmin=0,vmax=1, cmap="bwr_r")
ax.set_title("{}, r={} (isccw={})".format(pathstitle[j],radii[i], isccw) )

plt.tight_layout()
plt.show()

enter image description here

关于python - matplotlib.Path.contains_points : "radius" parameter defined inconsistently,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45957229/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com