如何绘制随机平面

16

我正在使用以下代码绘制随机平面,这些平面在三维空间中经过原点。

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#Number of hyperplanes
n = 20
#Dimension of space
d = 3

plt3d = plt.figure().gca(projection='3d')
for i in xrange(n):
    #Create random point on unit sphere
    v = np.random.normal(size = d)
    v = v/np.sqrt(np.sum(v**2))
    # create x,y
    xx, yy = np.meshgrid(range(-5,5), range(-5,5))
    z = (-v[0] * xx - v[1] * yy)/v[2]
    # plot the surface
    plt3d.plot_surface(xx, yy, z, alpha = 0.5)
plt.show()

但是从图片来看,我不相信它们是均匀选择的。我做错了什么?


6
热死了!一个真正的最小工作示例。点个赞。 - Veedrac
3
@Veedrac,你不能用一个单一的法向量来定义一个通过原点的平面吗?请参见http://math.stackexchange.com/questions/952525/select-a-random-hyperplane。此外,我正在使用的方法是在http://mathworld.wolfram.com/SpherePointPicking.html中描述的“选择随机点的另一种简单方法”。 - Simd
啊,你两点都说对了。我真是太傻了。 - Veedrac
4个回答

4

我建议您检查一下坐标轴。您的计算使得Z轴过于大,这意味着您有一个非常偏颇的观点。

首先,请确保您的法线在圆上均匀分布:

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#Number of hyperplanes
n = 1000
#Dimension of space
d = 3

plt3d = plt.figure().gca(projection='3d')
for i in xrange(n):
    #Create random point on unit sphere
    v = np.random.normal(size = d)
    v = v/np.sqrt(np.sum(v**2))
    v *= 10

    plt3d.scatter(v[0], v[1], v[2])

plt3d.set_aspect(1)
plt3d.set_xlim(-10, 10)
plt3d.set_ylim(-10, 10)
plt3d.set_zlim(-10, 10)

plt.show()

法线周围的一组点

然后检查您的平面是否被正确创建:

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#Number of hyperplanes
n = 1
#Dimension of space
d = 3

plt3d = plt.figure().gca(projection='3d')
for i in xrange(n):
    #Create random point on unit sphere
    v = np.random.normal(size = d)
    v = v/np.sqrt(np.sum(v**2))
    v *= 10

    # create x,y
    xx, yy = np.meshgrid(np.arange(-5,5,0.3), np.arange(-5,5,0.3))
    xx = xx.flatten()
    yy = yy.flatten()
    z = (-v[0] * xx - v[1] * yy)/v[2]

    # Hack to keep the plane small
    filter = xx**2 + yy**2 + z**2 < 5**2
    xx = xx[filter]
    yy = yy[filter]
    z = z[filter]

    # plot the surface
    plt3d.scatter(xx, yy, z, alpha = 0.5)

    for i in np.arange(0.1, 1, 0.1):
        plt3d.scatter(i*v[0], i*v[1], i*v[2])

plt3d.set_aspect(1)
plt3d.set_xlim(-10, 10)
plt3d.set_ylim(-10, 10)
plt3d.set_zlim(-10, 10)

plt.show()

一种卫星接收器...有点像这样

然后您就可以看到您实际上得到了良好的结果!

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#Number of hyperplanes
n = 100
#Dimension of space
d = 3

plt3d = plt.figure().gca(projection='3d')
for i in xrange(n):
    #Create random point on unit sphere
    v = np.random.normal(size = d)
    v = v/np.sqrt(np.sum(v**2))
    v *= 10

    # create x,y
    xx, yy = np.meshgrid(np.arange(-5,5,0.3), np.arange(-5,5,0.3))
    xx = xx.flatten()
    yy = yy.flatten()
    z = (-v[0] * xx - v[1] * yy)/v[2]

    # Hack to keep the plane small
    filter = xx**2 + yy**2 + z**2 < 5**2
    xx = xx[filter]
    yy = yy[filter]
    z = z[filter]

    # plot the surface
    plt3d.scatter(xx, yy, z, alpha = 0.5)

plt3d.set_aspect(1)
plt3d.set_xlim(-10, 10)
plt3d.set_ylim(-10, 10)
plt3d.set_zlim(-10, 10)

plt.show()

It's a sphere made of spherically bound planes!


4

你的代码正在生成具有随机分布法线的平面。它们看起来不是那样,因为z轴比x轴和y轴大得多。

通过生成在平面上均匀分布的点,可以生成更好的外观图像。为此,使用新坐标(u,v)对平面进行参数化,然后在一致间隔的(u,v)点网格上对平面进行采样。然后将这些(u,v)点转换为(x,y,z)空间中的点。

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import math
import itertools as IT

def points_on_sphere(dim, N, norm=np.random.normal):
    """
    http://en.wikipedia.org/wiki/N-sphere#Generating_random_points
    """
    normal_deviates = norm(size=(N, dim))
    radius = np.sqrt((normal_deviates ** 2).sum(axis=0))
    points = normal_deviates / radius
    return points

# Number of hyperplanes
n = 10
# Dimension of space
d = 3

fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
points = points_on_sphere(n, d).T
uu, vv = np.meshgrid([-5, 5], [-5, 5], sparse=True)
colors = np.linspace(0, 1, len(points))
cmap = plt.get_cmap('jet')
for nhat, c in IT.izip(points, colors):
    u = (0, 1, 0) if np.allclose(nhat, (1, 0, 0)) else np.cross(nhat, (1, 0, 0))
    u /= math.sqrt((u ** 2).sum())
    v = np.cross(nhat, u)
    u = u[:, np.newaxis, np.newaxis]
    v = v[:, np.newaxis, np.newaxis]
    xx, yy, zz = u * uu + v * vv
    ax.plot_surface(xx, yy, zz, alpha=0.5, color=cmap(c))
ax.set_xlim3d([-5,5])
ax.set_ylim3d([-5,5])
ax.set_zlim3d([-5,5])        
plt.show()

enter image description here

或者,您可以使用Till Hoffmann的pathpatch_2d_to_3d实用程序函数来避免复杂的数学计算:

for nhat, c in IT.izip(points, colors):
    p = patches.Rectangle((-2.5, -2.5), 5, 5, color=cmap(c), alpha=0.5)
    ax.add_patch(p)
    pathpatch_2d_to_3d(p, z=0, normal=nhat)

ax.set_xlim3d([-5,5])
ax.set_ylim3d([-5,5])
ax.set_zlim3d([-5,5])        
plt.show()

enter image description here


我非常喜欢你链接到了pathpatch_2d_to_3d,谢谢! - Simd

3

看起来并不是一切。下次最好测量一下 :-]。由于你没有固定坐标轴,它似乎是非随机分布的。因此,您会看到一个主平面,它一直延伸到天空,而其余的部分由于比例尺,看起来非常相似且不是随机分布的。

这段代码怎么样:

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#Number of hyperplanes
n = 20
#Dimension of space
d = 3

plt3d = plt.figure().gca(projection='3d')
for i in xrange(n):
    #Create random point on unit sphere
    v = np.random.normal(size = d)
    v = v/np.sqrt(np.sum(v**2))
    # create x,y
    xx, yy = np.meshgrid(range(-1,1), range(-1,1))
    z = (-v[0] * xx - v[1] * yy)/v[2]
    # plot the surface
    plt3d.plot_surface(xx, yy, z, alpha = 0.5)

plt3d.set_xlim3d([-1,1])
plt3d.set_ylim3d([-1,1])
plt3d.set_zlim3d([-1,1])
plt.show()

虽然不是完美的,但现在似乎更加随机了...


我所使用的方法来在球面上随机选择点是在 http://mathworld.wolfram.com/SpherePointPicking.html 的末尾。然后,按照 http://math.stackexchange.com/questions/952525/select-a-random-hyperplane 定义了平面。显然,我做错了什么。 - Simd
这很有道理,但说实话,我并不真正看到它如何变成均匀分布... - Jendas
@Jendas 哈哈!看起来你比我早一个小时得到了答案,而不是3分钟!好吧,这个的输出结果有点混乱。这是你得到的结果吗?链接 - Veedrac
是的,我只是一直在编辑已删除的答案,最终到了这里。我也看到了混乱,但我专注于尽可能保持原始代码不受影响,以便提问者更好地理解......而且他还质疑了平面的均匀分布,这才是最重要的。 - Jendas

1
我试过这种方法,也许这是创建均匀平面的更好方式。我随机选择两个不同的角度用于球坐标系,并将其转换为笛卡尔坐标系以获得平面的法向量。此外,在绘图时,您应该注意您的平面中心点不在原点上。
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = Axes3D(fig)

for i in range(20):
    theta = 2*np.pi*np.random.uniform(-1,1)    
    psi = 2*np.pi*np.random.uniform(-1,1)
    normal = np.array([np.sin(theta)*np.cos(psi),np.sin(theta)*np.sin(psi),
                       np.cos(theta)])
    xx, yy = np.meshgrid(np.arange(-1,1), np.arange(-1,1))
    z = (-normal[0] * xx - normal[1] * yy)/normal[2]
    ax.plot_surface(xx, yy, z, alpha=0.5)    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接