matplotlib.Path.contains_点:“radius”参数定义不一致

2024-05-23 19:24:02 发布

您现在位置:Python中文网/ 问答频道 /正文

问题:

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

简而言之:

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

示例:

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

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

代码:

^{1}$

输出:

^{pr2}$

凸路径的解:

我唯一的想法是,把路径强制成逆时针方向,并根据这个方向使用半径。在

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参数,或者使用预定义函数查找路径的方向。在

Tags: path路径truenp半径方向pointcontains
1条回答
网友
1楼 · 发布于 2024-05-23 19:24:02

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

然后将radius定义为

  • 当路径逆时针方向时展开路径,然后
  • 当路径顺时针旋转时收缩路径

下面的示例中显示了这一点,其中,对于(逆时针)顺时针方向的路径,将绘制展开/避开区域中包含的点。(红色=not contains_point,蓝色=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.5和{}之间的点,而不是{}和{}之间的点。在

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

一种选择可能是检查路径是否“主要是逆时针方向”。在

^{pr2}$

这样就可以在“大部分是顺时针”路径的情况下调整radius

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

相关问题 更多 >